pax_global_header00006660000000000000000000000064144061511020014504gustar00rootroot0000000000000052 comment=c7e430215fc1d4643db2470ba0a2f564e9633e43 ipmctl-03.00.00.0485/000077500000000000000000000000001440615110200136135ustar00rootroot00000000000000ipmctl-03.00.00.0485/.gitattributes000066400000000000000000000004171440615110200165100ustar00rootroot00000000000000*.uni diff=word # Set the default behavior, in case people don't have core.autocrlf set. * text=auto src/os/** text eol=lf Dcpmpkg/** text eol=lf CMakeLists.txt text eol=lf CMake/** text eol=lf install/linux/logrotate/** text eol=lf install/linux/debian/** text eol=lf ipmctl-03.00.00.0485/CMake/000077500000000000000000000000001440615110200145735ustar00rootroot00000000000000ipmctl-03.00.00.0485/CMake/Find/000077500000000000000000000000001440615110200154535ustar00rootroot00000000000000ipmctl-03.00.00.0485/CMake/Find/Finda2x.cmake000066400000000000000000000005241440615110200177510ustar00rootroot00000000000000# Copyright (c) 2018, Intel Corporation. # SPDX-License-Identifier: BSD-3-Clause # Try to find a2x # Once done this will define # A2X_FOUND - a2x found find_program(A2X_BINARY NAMES a2x HINTS ${A2X_BINARY_PATH}) find_package_handle_standard_args(a2x DEFAULT_MSG A2X_BINARY) mark_as_advanced(A2X_BINARY)ipmctl-03.00.00.0485/CMake/Find/Findasciidoc.cmake000066400000000000000000000006021440615110200210320ustar00rootroot00000000000000# Copyright (c) 2018, Intel Corporation. # SPDX-License-Identifier: BSD-3-Clause # Try to find asciidoc # Once done this will define # ASCIIDOC_FOUND - asciidoc found find_program(ASCIIDOC_BINARY NAMES asciidoc HINTS ${ASCIIDOC_BINARY_PATH}) find_package_handle_standard_args(asciidoc DEFAULT_MSG ASCIIDOC_BINARY) mark_as_advanced(ASCIIDOC_BINARY)ipmctl-03.00.00.0485/CMake/Find/Findasciidoctor-pdf.cmake000066400000000000000000000017321440615110200223330ustar00rootroot00000000000000# Copyright (c) 2018, Intel Corporation. # SPDX-License-Identifier: BSD-3-Clause # Try to find asciidoctor-pdf # Once done this will define # ASCIIDOCTOR-PDF_FOUND - asciidoctor-pdf found find_program(ASCIIDOCTOR_PDF_BINARY NAMES asciidoctor-pdf HINTS ${ASCIIDOCTOR_PDF_BINARY_PATH}) # Get the version of asciidoctor-pdf execute_process( COMMAND ${ASCIIDOCTOR_PDF_BINARY} --version OUTPUT_VARIABLE stdout_str ) separate_arguments(stdout_str) if (NOT "" STREQUAL "${stdout_str}") # Only parse stdout_str if found # Example output: "Asciidoctor PDF 1.5.3 using Asciidoctor 2.0.10 [https://asciidoctor.org]" # The argument at position 2 is the version list(GET stdout_str 2 ASCIIDOCTOR_PDF_VERSION) endif() find_package_handle_standard_args(asciidoctor-pdf REQUIRED_VARS ASCIIDOCTOR_PDF_BINARY VERSION_VAR ASCIIDOCTOR_PDF_VERSION ) mark_as_advanced(ASCIIDOCTOR_PDF_BINARY)ipmctl-03.00.00.0485/CMake/Find/Findasciidoctor.cmake000066400000000000000000000016111440615110200215600ustar00rootroot00000000000000# Copyright (c) 2018, Intel Corporation. # SPDX-License-Identifier: BSD-3-Clause # Try to find asciidoctor # Once done this will define # ASCIIDOCTOR_FOUND - asciidoctor found find_program(ASCIIDOCTOR_BINARY NAMES asciidoctor HINTS ${ASCIIDOCTOR_BINARY_PATH}) # Get the version of asciidoctor execute_process( COMMAND ${ASCIIDOCTOR_BINARY} --version OUTPUT_VARIABLE stdout_str ) separate_arguments(stdout_str) if (NOT "" STREQUAL "${stdout_str}") # Only parse stdout_str if found # Example output: "Asciidoctor 2.0.10 [https://asciidoctor.org]" # The argument at position 1 is the version list(GET stdout_str 1 ASCIIDOCTOR_VERSION) endif() find_package_handle_standard_args(asciidoctor REQUIRED_VARS ASCIIDOCTOR_BINARY VERSION_VAR ASCIIDOCTOR_VERSION ) mark_as_advanced(ASCIIDOCTOR_BINARY)ipmctl-03.00.00.0485/CMake/GTest.txt.in000066400000000000000000000006541440615110200167740ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.2) project(googletest-download NONE) include(ExternalProject) ExternalProject_Add(googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.8.0 SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" TEST_COMMAND "" ) ipmctl-03.00.00.0485/CMake/unit_test.cmake000066400000000000000000000033211440615110200176120ustar00rootroot00000000000000SET(CMAKE_SKIP_BUILD_RPATH FALSE) # -------------------------------------------------------------------------------------------------- # Google Test and Google Mock # -------------------------------------------------------------------------------------------------- # Download and unpack googletest at configure time configure_file(CMake/GTest.txt.in ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt) execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} . RESULT_VARIABLE result WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) if(result) message(FATAL_ERROR "CMake step for googletest failed: ${result}") endif() execute_process(COMMAND ${CMAKE_COMMAND} --build . RESULT_VARIABLE result WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) if(result) message(FATAL_ERROR "Build step for googletest failed: ${result}") endif() # Prevent overriding the parent project's compiler/linker # settings on Windows set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Add googletest directly to our build. This defines # the gtest and gtest_main targets. add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src ${CMAKE_BINARY_DIR}/googletest-build EXCLUDE_FROM_ALL ) # -------------------------------------------------------------------------------------------------- # libipmctl tests # -------------------------------------------------------------------------------------------------- file(GLOB CORE_TEST_SRC src/os/nvm_api/unittest/* ) message(TESTS: ${CORE_TEST_SRC}) add_executable(ipmctl_test ${CORE_TEST_SRC}) target_link_libraries(ipmctl_test gtest gtest_main gmock ipmctl ) include_directories(ipmctl_test SYSTEM PUBLIC src/os/nvm_api ) ipmctl-03.00.00.0485/CMakeLists.txt000066400000000000000000000666041440615110200163670ustar00rootroot00000000000000# Copyright (c) 2018, Intel Corporation. # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 2.8.12) message("cmake version: " ${CMAKE_VERSION}) project(ipmctl) # Enable tests that may be contained in the below subdirectories enable_testing() set(CMAKE_VERBOSE_MAKEFILE on) get_filename_component(ROOT ./ ABSOLUTE) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake/Find") set(LIBIPMCTL_VERSION_MAJOR 5) set(LIBIPMCTL_VERSION_MINOR 2) set(LIBIPMCTL_VERSION_PATCH 0) set(LIBIPMCTL_VERSION_STRING ${LIBIPMCTL_VERSION_MAJOR}.${LIBIPMCTL_VERSION_MINOR}.${LIBIPMCTL_VERSION_PATCH}) set(IPMCTL_VERSION_MAJOR 03) set(IPMCTL_VERSION_MINOR 01) set(IPMCTL_VERSION_PATCH 00) set(IPMCTL_SPEC_VERSION_MAJOR 3) set(IPMCTL_SPEC_VERSION_MINOR 03) set(IPMCTL_DRAFT "") set(IPMCTL_SPEC_VERSION_STRING ${IPMCTL_SPEC_VERSION_MAJOR}.${IPMCTL_SPEC_VERSION_MINOR}${IPMCTL_DRAFT}) if(BUILDNUM) set(IPMCTL_VERSION_STRING ${BUILDNUM}) else() find_package(Git) if(GIT_FOUND) execute_process(COMMAND ${GIT_EXECUTABLE} describe --tag --dirty OUTPUT_VARIABLE IPMCTL_VERSION_STRING RESULT_VARIABLE GIT_RETURN_CODE ) # strip leading string in git tag string(REGEX MATCH "([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)" IPMCTL_VERSION_STRING "${IPMCTL_VERSION_STRING}") # strip ending newline string(REGEX REPLACE "\n" "" IPMCTL_VERSION_STRING "${IPMCTL_VERSION_STRING}") endif() if(NOT GIT_FOUND OR ${GIT_RETURN_CODE}) set(IPMCTL_VERSION_STRING ${IPMCTL_VERSION_MAJOR}.${IPMCTL_VERSION_MINOR}.${IPMCTL_VERSION_PATCH}.0483) endif() endif() message(VERSION: ${IPMCTL_VERSION_STRING}) if(RELEASE OR CMAKE_BUILD_TYPE STREQUAL "Release") set(BUILD_TYPE release) set(CMAKE_BUILD_TYPE Release) SET(CMAKE_SKIP_BUILD_RPATH TRUE) else() set(BUILD_TYPE debug) set(CMAKE_BUILD_TYPE Debug) add_definitions( -DDEBUG_BUILD ) endif() message(CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}) if(BUILD_STATIC) set(LIB_TYPE STATIC) else() set(LIB_TYPE SHARED) endif() find_package(PythonInterp REQUIRED) if(ESX_BUILD) set(OS_TYPE esx) set(FILE_PREFIX esx) set(CMAKE_SKIP_BUILD_RPATH FALSE) set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) set(CMAKE_INSTALL_RPATH "/opt/intel/bin") else() ## Common packages between Windows and Linux # Optional packages for documentation ## Unfortunately it sounds like we can't do conditional inclusion ## based on the target selected. find_package(a2x) find_package(asciidoctor) find_package(asciidoc) if(UNIX) set(LNX_BUILD 1) set(OS_TYPE linux) set(FILE_PREFIX lnx) find_package(PkgConfig REQUIRED) pkg_check_modules(NDCTL REQUIRED libndctl>=58.2) pkg_check_modules(SYSTEMD systemd) find_package(Threads REQUIRED) elseif(MSVC) set(WIN_BUILD 1) set(OS_TYPE win) set(FILE_PREFIX win) endif() endif() message(BUILD_TYPE: ${CMAKE_BUILD_TYPE}) message(OS_TYPE: ${OS_TYPE}) message(LIB_TYPE: ${LIB_TYPE}) message(COMPILER: ${CMAKE_C_COMPILER}) message(SYSROOT: ${MY_CMAKE_SYSROOT}) message(TOOLCHAIN: ${CMAKE_TOOLCHAIN_FILE}) set(OUTPUT_DIR ${ROOT}/output/${BUILD_TYPE}) file(MAKE_DIRECTORY ${OUTPUT_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${ROOT}/output/${BUILD_TYPE}) set(CMAKE_BINARY_DIR ${OUTPUT_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) if(INTERNAL_MFG) include(CMake/mfg.cmake) endif() #---------------------------------------------------------------------------------------------------- # Project wide defines and flags #---------------------------------------------------------------------------------------------------- add_definitions( -DOS_BUILD -DPCD_CACHE_ENABLED -D__VERSION_NUMBER__=${IPMCTL_VERSION_STRING} -DPLAYBACK_RECORD_SUPPORTED ) # Promote warnings to errors only for release builds if(MSVC) set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /WX") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /WX") set(CMAKE_C_FLAGS_DEBUG_ "${CMAKE_C_FLAGS_DEBUG} /Od") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od") else() set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2 -fno-strict-aliasing -D_FORTIFY_SOURCE=2") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -fno-strict-aliasing -D_FORTIFY_SOURCE=2") if(LNX_BUILD) #A few warnings yet to resolve under esx set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Werror") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Werror") endif() set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -ggdb") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -ggdb") endif() if(UNIX) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_MSABI_VA_FUNCS -std=c99 -Wformat -Wformat-security -D_XOPEN_SOURCE=500 -Wall -Wfatal-errors -MMD -fPIC -fno-strict-aliasing") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wformat -Wformat-security -D_XOPEN_SOURCE=500 -Drestrict=__restrict__ -Wall -Wfatal-errors -MMD -fPIC -fno-strict-aliasing") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -z noexecstack -z relro -z now -pie") elseif(MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /GS /DynamicBase /sdl") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GS /DynamicBase /sdl") set(CMAKE_SHARED_LINKER_FLAGS "/NXCompat") endif() if(LNX_BUILD) if("${CMAKE_INSTALL_DATAROOTDIR}" STREQUAL "/usr/share") # Workaround for the RPM build cause the %{_datarootdir} is already prefixed set(INI_INSTALL_FILEPATH "${CMAKE_INSTALL_DATAROOTDIR}") else() if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/") # In case the CMAKE_INSTALL_PREFIX equals "/" for other than the SYSCONFDIR and LOCALSTATEDIR, # the value of CMAKE_INSTALL_ is prefixed with usr/ if it is not user-specified as an absolute path. set(CMAKE_INSTALL_PREFIX "/usr") message("CMAKE_INSTALL_PREFIX value changed to: ${CMAKE_INSTALL_PREFIX}") endif() if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR) set(CMAKE_INSTALL_DATAROOTDIR "share") message("CMAKE_INSTALL_DATAROOTDIR not defined. Creating a new definition with value: ${CMAKE_INSTALL_DATAROOTDIR}") endif() set(INI_INSTALL_FILEPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}") endif() message("INI_INSTALL_FILEPATH definition create with value: ${INI_INSTALL_FILEPATH}") add_definitions( -D_GNU_SOURCE -D__LINUX__ -DHAVE_C99 -DINI_INSTALL_FILEPATH="${INI_INSTALL_FILEPATH}/ipmctl/" ) if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 4.9) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong") endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-strong") endif() endif() if(MSVC) if(VCREDIST_STATIC) # Force vcredist to be linked static # Keep this after all other compiler flag setup set(CompilerFlags CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO ) foreach(CompilerFlag ${CompilerFlags}) string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") endforeach() message("VCREDIST: STATIC") else() message("VCREDIST: SHARED") endif() endif() #---------------------------------------------------------------------------------------------------- # OS driver interface library #---------------------------------------------------------------------------------------------------- if(MSVC) # setup resource file for file details string(REGEX REPLACE "[.]" "," IPMCTL_VERSION_COMMA_STRING "${IPMCTL_VERSION_STRING}") # exe set (NAME_RC ${CMAKE_PROJECT_NAME}) set (NAME_RC_EXT "${NAME_RC}.exe") set (VFT_TYPE "VFT_APP") configure_file(src/os/win/ipmctl.rc.in src/os/win/ipmctl.rc @ONLY) # lib set (NAME_RC "lib${CMAKE_PROJECT_NAME}") set (NAME_RC_EXT "${NAME_RC}.dll") set (VFT_TYPE "VFT_DLL") configure_file(src/os/win/ipmctl.rc.in src/os/win/libipmctl.rc @ONLY) FILE(GLOB OS_INTERFACE_SOURCE_FILES src/os/os_str.c src/os/os_common.c src/os/win/win_scm2_ioctl.c src/os/win/win_scm2_passthrough.c src/os/win/win_scm2_ioctl_passthrough.c src/os/win/win_common.c src/os/win/win_api.c src/os/win/win_scm2_adapter.c src/os/win/win_system.c ) elseif(UNIX) FILE(GLOB OS_INTERFACE_SOURCE_FILES src/os/os_str.c src/os/os_common.c src/os/${OS_TYPE}/${FILE_PREFIX}_adapter_passthrough.c src/os/${OS_TYPE}/${FILE_PREFIX}_acpi.c src/os/${OS_TYPE}/${FILE_PREFIX}_common.c src/os/${OS_TYPE}/${FILE_PREFIX}_api.c src/os/${OS_TYPE}/${FILE_PREFIX}_adapter.c src/os/${OS_TYPE}/${FILE_PREFIX}_system.c ) endif() add_library(ipmctl_os_interface STATIC ${OS_INTERFACE_SOURCE_FILES}) target_link_libraries(ipmctl_os_interface ${CMAKE_THREAD_LIBS_INIT} ) target_include_directories(ipmctl_os_interface PUBLIC src/os src/os/${OS_TYPE} DcpmPkg/common DcpmPkg/cli src/os/nvm_api src/os/s_string MdePkg/Include MdePkg/Include/Uefi src/os/efi_shim ) if (MSVC) string(REGEX REPLACE "([0-9]+)\\.[0-9]+\\.[0-9]+\\.[0-9]+" "\\1" sdk_version_major $ENV{WindowsSDKVersion}) string(REGEX REPLACE "[0-9]+\\.([0-9]+)\\.[0-9]+\\.[0-9]+" "\\1" sdk_version_major2 $ENV{WindowsSDKVersion}) string(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+)\\.[0-9]+" "\\1" sdk_version_minor $ENV{WindowsSDKVersion}) if (${sdk_version_major} GREATER 9 AND ${sdk_version_minor} GREATER 17650) MESSAGE("Using Windows SDK 17650+") else() MESSAGE(FATAL_ERROR "Requires Windows SDK version 17650 or newer.") endif() endif() #---------------------------------------------------------------------------------------------------- # libipmctl #---------------------------------------------------------------------------------------------------- file(GLOB LIBIPMCTL_SOURCE_FILES src/os/efi_shim/AutoGen.c src/os/efi_shim/AutoGenIo.c src/os/efi_shim/os_efi_api.c src/os/efi_shim/os_efi_api_io.c src/os/efi_shim/os_efi_preferences.c src/os/efi_shim/os_efi_shell_parameters_protocol.c src/os/efi_shim/os_efi_simple_file_protocol.c src/os/efi_shim/os_efi_bs_protocol.c src/os/ini/ini.c src/os/eventlog/event.c src/os/nvm_api/nvm_management.c src/os/nvm_api/nvm_output_parsing.c src/os/s_string/s_str.c DcpmPkg/cli/NvmDimmCli.c DcpmPkg/cli/CommandParser.c DcpmPkg/cli/Common.c DcpmPkg/cli/ShowAcpiCommand.c DcpmPkg/cli/ShowSensorCommand.c DcpmPkg/cli/ShowDimmsCommand.c DcpmPkg/cli/ShowSocketsCommand.c DcpmPkg/cli/ShowMemoryResourcesCommand.c DcpmPkg/cli/ShowSystemCapabilitiesCommand.c DcpmPkg/cli/ShowFirmwareCommand.c DcpmPkg/cli/ShowPcdCommand.c DcpmPkg/cli/DeletePcdCommand.c DcpmPkg/cli/ShowRegionsCommand.c DcpmPkg/cli/CreateGoalCommand.c DcpmPkg/cli/ShowGoalCommand.c DcpmPkg/cli/DeleteGoalCommand.c DcpmPkg/cli/ShowErrorCommand.c DcpmPkg/cli/ShowCelCommand.c DcpmPkg/cli/DumpDebugCommand.c DcpmPkg/cli/StartDiagnosticCommand.c DcpmPkg/cli/ShowPreferencesCommand.c DcpmPkg/cli/ShowTopologyCommand.c DcpmPkg/cli/SetPreferencesCommand.c DcpmPkg/cli/SetSensorCommand.c DcpmPkg/cli/LoadGoalCommand.c DcpmPkg/cli/DumpGoalCommand.c DcpmPkg/cli/SetDimmCommand.c DcpmPkg/cli/LoadCommand.c DcpmPkg/cli/DeleteDimmCommand.c src/os/cli_cmds/DumpSupportCommand.c DcpmPkg/cli/ShowRegisterCommand.c DcpmPkg/cli/StartFormatCommand.c DcpmPkg/cli/ShowPerformanceCommand.c DcpmPkg/cli/ShowHostServerCommand.c DcpmPkg/cli/ShowCmdAccessPolicyCommand.c DcpmPkg/cli/StartSessionCommand.c DcpmPkg/cli/StopSessionCommand.c DcpmPkg/cli/LoadSessionCommand.c DcpmPkg/cli/ShowSessionCommand.c DcpmPkg/cli/DumpSessionCommand.c DcpmPkg/common/FwUtility.c DcpmPkg/common/Utility.c DcpmPkg/common/UtilityIo.c DcpmPkg/common/NvmTables.c DcpmPkg/common/ShowAcpi.c DcpmPkg/common/NvmStatus.c DcpmPkg/common/NvmHealth.c DcpmPkg/common/LbaCommon.c DcpmPkg/common/Convert.c DcpmPkg/common/PcdCommon.c DcpmPkg/common/OsCommon.c DcpmPkg/common/DataSet.c DcpmPkg/common/Printer.c DcpmPkg/common/Strings.c DcpmPkg/common/Nlog.c DcpmPkg/common/ReadRunTimePreferences.c DcpmPkg/driver/Protocol/Driver/NvmDimmConfig.c DcpmPkg/driver/NvmDimmDriver.c DcpmPkg/driver/Core/Dimm.c DcpmPkg/driver/Core/Namespace.c DcpmPkg/driver/Core/NvmSecurity.c DcpmPkg/driver/Core/Region.c DcpmPkg/driver/Core/Btt.c DcpmPkg/driver/Core/Pfn.c DcpmPkg/driver/Core/Diagnostics/ConfigDiagnostic.c DcpmPkg/driver/Core/Diagnostics/CoreDiagnostics.c DcpmPkg/driver/Core/Diagnostics/FwDiagnostic.c DcpmPkg/driver/Core/Diagnostics/QuickDiagnostic.c DcpmPkg/driver/Core/Diagnostics/SecurityDiagnostic.c DcpmPkg/driver/Protocol/Device/NvmFirmwareManagement.c DcpmPkg/driver/Protocol/Namespace/NvmDimmBlockIo.c DcpmPkg/driver/Utils/PlatformConfigData.c DcpmPkg/driver/Utils/AcpiParsing.c DcpmPkg/driver/Utils/ProcessorAndTopologyInfo.c DcpmPkg/driver/Utils/Interleave.c DcpmPkg/driver/Utils/SmbiosUtility.c DcpmPkg/driver/Utils/DumpLoadRegions.c DcpmPkg/common/Pbr.c DcpmPkg/common/PbrDcpmm.c DcpmPkg/common/PbrOs.c MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.c MdePkg/Library/UefiDevicePathLib/DevicePathUtilities.c MdePkg/Library/UefiDevicePathLib/DevicePathToText.c MdePkg/Library/UefiDevicePathLib/DevicePathFromText.c MdePkg/Library/BaseLib/Math64.c MdePkg/Library/BaseLib/MultU64x32.c MdePkg/Library/BaseLib/DivU64x32Remainder.c MdePkg/Library/BaseLib/Unaligned.c MdePkg/Library/BaseLib/BitField.c MdePkg/Library/BaseLib/LinkedList.c MdePkg/Library/BaseLib/LShiftU64.c MdePkg/Library/BaseLib/Math64.c MdePkg/Library/BaseLib/SwapBytes64.c MdePkg/Library/BaseLib/SwapBytes32.c MdePkg/Library/BaseLib/SwapBytes16.c MdePkg/Library/BaseLib/String.c MdePkg/Library/BaseLib/SafeString.c MdePkg/Library/BaseLib/RShiftU64.c MdePkg/Library/BaseLib/Math64.c MdePkg/Library/BaseLib/DivU64x32.c ${LIBIPMCTL_MFG_FILES} ) # OS Files list(APPEND LIBIPMCTL_SOURCE_FILES src/os/efi_shim/${FILE_PREFIX}_efi_api.c ) # if on Windows add rc file for file details if (MSVC) list(APPEND LIBIPMCTL_SOURCE_FILES ${CMAKE_CURRENT_BINARY_DIR}/src/os/win/libipmctl.rc ) endif() add_library(ipmctl ${LIB_TYPE} ${LIBIPMCTL_SOURCE_FILES}) target_include_directories(ipmctl PUBLIC DcpmPkg/cli src/os src/os/efi_shim src/os/ini src/os/eventlog src/os/nvm_api src/os/s_string src/os/cli_cmds DcpmPkg/common DcpmPkg/driver DcpmPkg/driver/Utils DcpmPkg/driver/Protocol/Driver DcpmPkg/driver/Core DcpmPkg/driver/Protocol/Namespace DcpmPkg/driver/Protocol/Device DcpmPkg/driver/Core/Diagnostics MdePkg MdePkg/Include MdePkg/Include/Protocol MdePkg/Include/Uefi MdePkg/Include/Library ShellPkg ShellPkg/Include ShellPkg/Include/Protocol ShellPkg/Include/Library ShellPkg/Library MdeModulePkg MdeModulePkg/Include MdeModulePkg/Include/Library ) if(MSVC) set_target_properties(ipmctl PROPERTIES PREFIX "lib") target_compile_options(ipmctl PRIVATE /FIAutoGen.h -D__NVM_DLL__ -D__NVM_API_DLL_EXPORTS__ ) # FIXME: Ignore warnings for secure functions SET_SOURCE_FILES_PROPERTIES(src/os/efi_shim/os_efi_simple_file_protocol.c PROPERTIES COMPILE_FLAGS -D_CRT_SECURE_NO_WARNINGS) SET_SOURCE_FILES_PROPERTIES(src/os/eventlog/event.c PROPERTIES COMPILE_FLAGS -D_CRT_SECURE_NO_WARNINGS) SET_SOURCE_FILES_PROPERTIES(src/os/ini/ini.c PROPERTIES COMPILE_FLAGS -D_CRT_SECURE_NO_WARNINGS) SET_SOURCE_FILES_PROPERTIES(src/os/efi_shim/os_efi_preferences.c PROPERTIES COMPILE_FLAGS -D_CRT_SECURE_NO_WARNINGS) SET_SOURCE_FILES_PROPERTIES(src/os/nvm_api/nvm_management.c PROPERTIES COMPILE_FLAGS -D_CRT_SECURE_NO_WARNINGS) SET_SOURCE_FILES_PROPERTIES(DcpmPkg/cli/Common.c PROPERTIES COMPILE_FLAGS -D_CRT_SECURE_NO_WARNINGS) SET_SOURCE_FILES_PROPERTIES(src/os/nvm_api/nvm_output_parsing.c PROPERTIES COMPILE_FLAGS -D_CRT_SECURE_NO_WARNINGS) SET_SOURCE_FILES_PROPERTIES(src/os/efi_shim/os_efi_shell_parameters_protocol.c PROPERTIES COMPILE_FLAGS -D_CRT_SECURE_NO_WARNINGS) SET_SOURCE_FILES_PROPERTIES(src/os/cli_cmds/DumpSupportCommand.c PROPERTIES COMPILE_FLAGS -D_CRT_SECURE_NO_WARNINGS) else() target_compile_options(ipmctl PRIVATE -include AutoGen.h -D__NVM_DLL__ ) endif() add_dependencies(ipmctl stringdefs iniconfig ) target_link_libraries(ipmctl ipmctl_os_interface ) if(LNX_BUILD) target_link_libraries(ipmctl ${NDCTL_LIBRARIES} ) endif() set_target_properties(ipmctl PROPERTIES VERSION ${LIBIPMCTL_VERSION_STRING} SOVERSION ${LIBIPMCTL_VERSION_MAJOR} ) if(UNIX) set_target_properties(ipmctl_os_interface PROPERTIES COMPILE_FLAGS -fvisibility=hidden) set_target_properties(ipmctl PROPERTIES COMPILE_FLAGS -fvisibility=hidden) endif() #--------------------------------------------------------------------------------------------------- # ipmctl executable #--------------------------------------------------------------------------------------------------- FILE(GLOB IPMCTL_SOURCE_FILES src/os/os_main.c ) # if on Windows add rc file for file details if (MSVC) list(APPEND IPMCTL_SOURCE_FILES ${CMAKE_CURRENT_BINARY_DIR}/src/os/win/ipmctl.rc ) endif() add_executable(ipmctl-bin ${IPMCTL_SOURCE_FILES}) target_link_libraries(ipmctl-bin ipmctl ) target_include_directories(ipmctl-bin PUBLIC src/os src/os/nvm_api src/os/ini ) set_target_properties(ipmctl-bin PROPERTIES OUTPUT_NAME ipmctl ) if ((ASCIIDOCTOR_FOUND OR A2X_FOUND) AND LNX_BUILD) add_dependencies(ipmctl-bin manpage ) endif() if(MSVC) set_target_properties(ipmctl-bin PROPERTIES LINK_FLAGS "/STACK:3000000") else() target_compile_options(ipmctl-bin PRIVATE "-fPIE") set_target_properties(ipmctl-bin PROPERTIES LINK_FLAGS "-pie") endif() #---------------------------------------------------------------------------------------------------- # Generate String Definitions #---------------------------------------------------------------------------------------------------- set(STRING_DEFS_INPUT_FILES ${ROOT}/DcpmPkg/common/NvmStatus.uni ${ROOT}/DcpmPkg/driver/Core/Diagnostics/DiagnosticsMessages.uni ${ROOT}/src/os/efi_shim/os_efi_hii_auto_gen_strings.py ${ROOT}/src/os/efi_shim/os_efi_hii_auto_gen_strings_ordered_dict.py ) set(STRING_DEFS_OUTPUT_FILES ${ROOT}/src/os/efi_shim/os_efi_hii_auto_gen_strings.h ${ROOT}/src/os/efi_shim/os_efi_hii_auto_gen_defs.h ) add_custom_target(stringdefs ALL DEPENDS ${STRING_DEFS_OUTPUT_FILES} ${STRING_DEFS_INPUT_FILES}) add_custom_command(OUTPUT ${STRING_DEFS_OUTPUT_FILES} COMMAND ${PYTHON_EXECUTABLE} ${ROOT}/src/os/efi_shim/os_efi_hii_auto_gen_strings.py COMMENT "Generating String Definitions" DEPENDS ${STRING_DEFS_INPUT_FILES} ) #---------------------------------------------------------------------------------------------------- # Generate INI Default Config File #---------------------------------------------------------------------------------------------------- add_custom_target(iniconfig ALL DEPENDS ${OUTPUT_DIR}/ipmctl_default.conf ) if(MSVC) set(PRECOMPILER_FLAG /EP) else() set(PRECOMPILER_FLAG -E -P -D__LINUX__) endif() add_custom_command(OUTPUT ${OUTPUT_DIR}/ipmctl_default.conf COMMAND ${CMAKE_C_COMPILER} ${MY_CMAKE_SYSROOT} ${PRECOMPILER_FLAG} ${ROOT}/src/os/ini/ipmctl_default.c > ${ROOT}/src/os/ini/ipmctl_default.i COMMAND ${PYTHON_EXECUTABLE} ${ROOT}/src/os/ini/ini_auto_gen_default_config.py ${ROOT}/src/os/ini/ipmctl_default.i ${OUTPUT_DIR}/ipmctl_default.conf COMMAND ${CMAKE_COMMAND} -E remove -f ${ROOT}/src/os/ini/ipmctl_default.i COMMENT "Generating INI Default Config File" ) #---------------------------------------------------------------------------------------------------- # Install #---------------------------------------------------------------------------------------------------- FILE(GLOB NVM_HEADERS src/os/nvm_api/export_api.h src/os/nvm_api/nvm_management.h src/os/nvm_api/nvm_types.h DcpmPkg/common/NvmSharedDefs.h ) FILE(COPY opensource/opensource_LICENSE DESTINATION ${OUTPUT_DIR}) FILE(RENAME ${OUTPUT_DIR}/opensource_LICENSE ${OUTPUT_DIR}/LICENSE) FILE(COPY opensource/edk2_License.txt DESTINATION ${OUTPUT_DIR}) FILE(COPY opensource/thirdpartynotice.txt DESTINATION ${OUTPUT_DIR}) FILE(COPY ${NVM_HEADERS} DESTINATION ${OUTPUT_DIR}) FILE(COPY install/linux/logrotate/ipmctl.logrotate.conf DESTINATION ${OUTPUT_DIR}) if(LNX_BUILD) include(GNUInstallDirs) find_package(PkgConfig) pkg_check_modules(SYSTEMD systemd) configure_file(${ROOT}/install/linux/libipmctl.pc.in ${OUTPUT_DIR}/libipmctl.pc @ONLY) if(BUILD_STATIC) install(TARGETS ipmctl-bin RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) else() install(TARGETS ipmctl-bin ipmctl RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) endif() install(FILES ${OUTPUT_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/ipmctl ) install(FILES ${OUTPUT_DIR}/thirdpartynotice.txt DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/ipmctl ) install(FILES ${OUTPUT_DIR}/edk2_License.txt DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/ipmctl ) install(FILES ${OUTPUT_DIR}/ipmctl_default.conf DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/ipmctl ) install(FILES ${OUTPUT_DIR}/ipmctl_default.conf DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/ipmctl RENAME ipmctl.conf ) install(FILES ${NVM_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) file(MAKE_DIRECTORY output/ipmctl) install(DIRECTORY output/ipmctl DESTINATION ${CMAKE_INSTALL_LOCALSTATEDIR}/log ) install(FILES ${OUTPUT_DIR}/ipmctl.logrotate.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/logrotate.d RENAME ipmctl ) install(FILES ${OUTPUT_DIR}/libipmctl.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) if(ASCIIDOCTOR_FOUND OR A2X_FOUND) install(DIRECTORY ${OUTPUT_DIR}/manpage/ DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 ) endif() endif() #---------------------------------------------------------------------------------------------------- # Generate man pages with asciidoctor #---------------------------------------------------------------------------------------------------- if((ASCIIDOCTOR_FOUND OR A2X_FOUND) AND LNX_BUILD) file(MAKE_DIRECTORY ${OUTPUT_DIR}/manpage) file(MAKE_DIRECTORY ${OUTPUT_DIR}/docs) set(DOCUMENTATION_INPUT_FILES ${ROOT}/Documentation/ipmctl/ipmctl.txt ${ROOT}/Documentation/ipmctl/Discovery/ipmctl-show-dimm.txt ${ROOT}/Documentation/ipmctl/Discovery/ipmctl-show-memory-resources.txt ${ROOT}/Documentation/ipmctl/Discovery/ipmctl-show-socket.txt ${ROOT}/Documentation/ipmctl/Discovery/ipmctl-show-system-capabilities.txt ${ROOT}/Documentation/ipmctl/Discovery/ipmctl-show-topology.txt ${ROOT}/Documentation/ipmctl/Memory_Subsystem_Provisioning/ipmctl-create-goal.txt ${ROOT}/Documentation/ipmctl/Memory_Subsystem_Provisioning/ipmctl-delete-goal.txt ${ROOT}/Documentation/ipmctl/Memory_Subsystem_Provisioning/ipmctl-dump-goal.txt ${ROOT}/Documentation/ipmctl/Memory_Subsystem_Provisioning/ipmctl-load-goal.txt ${ROOT}/Documentation/ipmctl/Memory_Subsystem_Provisioning/ipmctl-show-goal.txt ${ROOT}/Documentation/ipmctl/Persistent_Memory_Provisioning/ipmctl-show-region.txt ${ROOT}/Documentation/ipmctl/Instrumentation/ipmctl-set-sensor.txt ${ROOT}/Documentation/ipmctl/Instrumentation/ipmctl-show-performance.txt ${ROOT}/Documentation/ipmctl/Instrumentation/ipmctl-show-sensor.txt ${ROOT}/Documentation/ipmctl/Support_and_Maintenance/ipmctl-set-preferences.txt ${ROOT}/Documentation/ipmctl/Support_and_Maintenance/ipmctl-dump-support-data.txt ${ROOT}/Documentation/ipmctl/Support_and_Maintenance/ipmctl-help.txt ${ROOT}/Documentation/ipmctl/Support_and_Maintenance/ipmctl-set-dimm.txt ${ROOT}/Documentation/ipmctl/Support_and_Maintenance/ipmctl-show-firmware.txt ${ROOT}/Documentation/ipmctl/Support_and_Maintenance/ipmctl-show-preferences.txt ${ROOT}/Documentation/ipmctl/Support_and_Maintenance/ipmctl-load-dimm.txt ${ROOT}/Documentation/ipmctl/Support_and_Maintenance/ipmctl-version.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-show-pcd.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-delete-pcd.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-dump-debug-log.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-inject-error.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-show-cap.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-show-cel.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-start-diagnostic.txt # ${ROOT}/Documentation/ipmctl/Debug/ipmctl-diagnostic-events.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-show-system.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-show-error-log.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-show-session.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-start-session.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-stop-session.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-load-session.txt ${ROOT}/Documentation/ipmctl/Debug/ipmctl-dump-session.txt ) list(LENGTH DOCUMENTATION_INPUT_FILES LEN) math(EXPR INPUT_LENGTH "${LEN} -1") foreach(INDEX RANGE ${INPUT_LENGTH}) list(GET DOCUMENTATION_INPUT_FILES ${INDEX} MAN_INPUT) string(REGEX MATCH "[^/]*$" MAN_BARE_FILENAME ${MAN_INPUT}) set(MAN_TMP_INPUT ${OUTPUT_DIR}/manpage/${MAN_BARE_FILENAME}) string(REGEX REPLACE "txt$" "1.gz" MAN_OUTPUT "${MAN_TMP_INPUT}") list(APPEND MANPAGE_OUTPUT_FILES ${MAN_OUTPUT}) if(NOT ASCIIDOCTOR_FOUND) string(REGEX REPLACE "\\.[^.]*$" "" MAN_GZ_INPUT ${MAN_OUTPUT}) add_custom_command(OUTPUT ${MAN_OUTPUT} COMMENT "Clean out link markup because they are invalid in the manpages" COMMAND sed -e 's/<>/]/g' ${MAN_INPUT} > ${MAN_TMP_INPUT} COMMAND ${A2X_BINARY} -f manpage -a mansource=ipmctl -a manmanual="ipmctl" -a ipmctl_version=${IPMCTL_VERSION_STRING} -a os_build=1 -a manpage=1 -D ${OUTPUT_DIR}/manpage ${MAN_TMP_INPUT} COMMAND gzip -f ${MAN_GZ_INPUT} COMMAND rm -f ${MAN_TMP_INPUT} COMMENT "Generating man pages" DEPENDS ${MAN_INPUT} ) endif() endforeach() add_custom_target(manpage ALL DEPENDS ${MANPAGE_OUTPUT_FILES} ${DOCUMENTATION_INPUT_FILES} ) if(ASCIIDOCTOR_FOUND) add_custom_command(OUTPUT ${MANPAGE_OUTPUT_FILES} COMMAND ${ASCIIDOCTOR_BINARY} -b manpage -a mansource=ipmctl -a manmanual="ipmctl" -a ipmctl_version=${IPMCTL_VERSION_STRING} -a os_build=1 -a manpage=1 -D ${OUTPUT_DIR}/manpage ${DOCUMENTATION_INPUT_FILES} COMMAND gzip -f ${OUTPUT_DIR}/manpage/*.1 COMMENT "Generating man pages" DEPENDS ${DOCUMENTATION_INPUT_FILES} ) endif() endif() # Allow building more documentation if we have access to it if(EXISTS "${ROOT}/CMake/internal.cmake") include("${ROOT}/CMake/internal.cmake") endif() # -------------------------------------------------------------------------------------------------- # Additional includes # -------------------------------------------------------------------------------------------------- # CppUTest unit tests # Ideally we want official builds to not include unit test executables and # have it easy to build unit tests as an individual target from development # environments. However, after some attempts of modifying linux_build.sh, I # still haven't found a solution. So if you want to run unit tests, just # uncomment this UNIT_TEST flag or define it using the build scripts for now. #set(UNIT_TEST on) if (UNIT_TEST) add_subdirectory("./tests/cpputest" "./cpputest_out") endif() if(LNX_BUILD AND UNIT_TEST) include(CMake/unit_test.cmake) endif() if(ESX_BUILD) include(CMake/esx.cmake) endif() add_subdirectory(src/os/nvm_api_sample)ipmctl-03.00.00.0485/DcpmPkg/000077500000000000000000000000001440615110200151405ustar00rootroot00000000000000ipmctl-03.00.00.0485/DcpmPkg/cli/000077500000000000000000000000001440615110200157075ustar00rootroot00000000000000ipmctl-03.00.00.0485/DcpmPkg/cli/CommandParser.c000066400000000000000000001743711440615110200206230ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "CommandParser.h" #include #include #include #include #include #include "Common.h" #include DispInfo gDisplayInfo; extern int g_basic_commands; /* local fns */ EFI_STATUS findVerb(UINTN *pStart, struct CommandInput *pInput, struct Command *pCommand); EFI_STATUS findOptions(UINTN *pStart, struct CommandInput *pInput, struct Command *pCommand); EFI_STATUS findTargets(UINTN *pStart, struct CommandInput *pInput, struct Command *pCommand); EFI_STATUS findProperties(UINTN *pStart, struct CommandInput *pInput, struct Command *pCommand); EFI_STATUS MatchCommand(struct Command *pInput, struct Command *pMatch); EFI_STATUS MatchOptions(struct Command *pInput, struct Command *pMatch); EFI_STATUS MatchTargets(struct Command *pInputCmd, struct Command *pCmdToMatch); EFI_STATUS MatchProperties(struct Command *pInput, struct Command *pMatch); static EFI_STATUS ValidateProtocolAndPayloadSizeOptions(struct Command *pCmd); UINT16 TargetCount(struct Command *pCmd); UINT16 TargetMatchCount(struct Command *pInputCmd, struct Command *pCmdToMatch); /* * Global variables */ static UINTN gCommandCount = 0; static struct Command *gCommandList = NULL; static CHAR16 *gSyntaxError = NULL; static UINTN gPossibleMatchCount = 0; static CHAR16 *gDetailedSyntaxError = NULL; /* * Add the specified command to the list of supported commands */ EFI_STATUS RegisterCommand(struct Command *pCommand) { EFI_STATUS Rc = EFI_SUCCESS; /* make sure a verb is specified */ if (NULL == pCommand || StrLen(pCommand->verb) == 0) { NVDIMM_WARN("Failed to register the command because it is invalid"); Rc = EFI_ABORTED; } else { /* allocate memory */ if (gCommandCount == 0) { gCommandList = AllocatePool(sizeof(struct Command)); } else { gCommandList = ReallocatePool(sizeof(struct Command) * gCommandCount, sizeof(struct Command) * (gCommandCount + 1), gCommandList); } if (gCommandList) { pCommand->CommandId = (UINT8)gCommandCount; // Save its index for better tracking. CopyMem_S(&gCommandList[gCommandCount], sizeof(struct Command), pCommand, sizeof(struct Command)); gCommandCount++; } else { NVDIMM_WARN("Failed to register the command due to lack of resources"); Rc = EFI_OUT_OF_RESOURCES; } } NVDIMM_EXIT_CHECK_I64(Rc); return Rc; } /** Free the allocated memory for target values in the CLI command structure. @param[in out] pCommand pointer to the command structure **/ VOID FreeCommandStructure( IN OUT COMMAND *pCommand ) { UINT32 Index = 0; if (pCommand != NULL) { for (Index = 0; Index < MAX_TARGETS; Index++) { FREE_POOL_SAFE(pCommand->targets[Index].pTargetValueStr); } for (Index = 0; Index < MAX_OPTIONS; Index++) { FREE_POOL_SAFE(pCommand->options[Index].pOptionValueStr); } } } /** The function parse the input string and split it to the tokens The caller function is responsible for deallocation of pCmdInput. The FreeCommandInput function should be used to deallocate memory. @param[in] pCommand The input string @param[out] pCmdInput **/ VOID FillCommandInput( IN CHAR16 *pCommand, OUT struct CommandInput *pCmdInput ) { if (pCommand == NULL || pCmdInput == NULL) { return; } pCmdInput->ppTokens = StrSplit(pCommand, L' ', &pCmdInput->TokenCount); } /** If parsing fails, retrieve a more useful syntax error **/ CHAR16 *getSyntaxError() { return gSyntaxError; } /** If parsing fails, set syntax error, but first free old one **/ VOID SetSyntaxError( IN CHAR16 *pSyntaxError ) { FREE_POOL_SAFE(gSyntaxError); gSyntaxError = pSyntaxError; } /** If parsing fails, set syntax error, but first free old one **/ VOID SetDetailedSyntaxError( IN CHAR16 *pDetailedSyntaxError ) { FREE_POOL_SAFE(gDetailedSyntaxError); gDetailedSyntaxError = pDetailedSyntaxError; } /* * Clean up the resources associated with the command list */ void FreeCommands() { NVDIMM_ENTRY(); gCommandCount = 0; FREE_POOL_SAFE(gCommandList); FREE_POOL_SAFE(gSyntaxError); FREE_POOL_SAFE(gDetailedSyntaxError); NVDIMM_EXIT(); } /* * Clean up the resources associated with the input */ void FreeCommandInput(struct CommandInput *pCommandInput) { NVDIMM_ENTRY(); if (pCommandInput == NULL) { return; } if (pCommandInput->ppTokens == NULL) { pCommandInput->TokenCount = 0; return; } FreeStringArray(pCommandInput->ppTokens, pCommandInput->TokenCount); pCommandInput->ppTokens = NULL; pCommandInput->TokenCount = 0; NVDIMM_EXIT(); } /* * Ensure cmd line args don't include '%' */ EFI_STATUS InvalidTokenScreen( IN struct CommandInput *pInput, IN CHAR16 *pHelpStr ) { UINTN Index = 0; CHAR16 *pTmpString = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; if (NULL == pInput || NULL == pHelpStr) { return ReturnCode; } for (Index = 0; Index < pInput->TokenCount; Index++) { if (NULL != StrStr(pInput->ppTokens[Index], L"%")) { pTmpString = CatSPrint(NULL, CLI_PARSER_ERR_UNEXPECTED_TOKEN, L"%%"); SetSyntaxError(CatSPrintClean(pTmpString, FORMAT_NL_STR FORMAT_NL_STR, CLI_PARSER_DID_YOU_MEAN, pHelpStr)); return ReturnCode; } } return EFI_SUCCESS; } extern BOOLEAN HelpRequested; /* * Parse the given the command line arguments to * identify the correct command. * * Parsing is a two step process to first identify the tokens of the input * and then try to match it against the list of supported commands. * * It is the responsibility of the caller function to free the allocated * memory for target values in the Command structure. */ EFI_STATUS Parse( IN struct CommandInput *pInput, IN OUT struct Command *pCommand ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINTN Start = 0; UINTN Index = 0; CHAR16 *pHelpStr = NULL; CHAR16 *pSyntaxErrorStr = NULL; NVDIMM_ENTRY(); FREE_POOL_SAFE(gSyntaxError); FREE_POOL_SAFE(gDetailedSyntaxError); gPossibleMatchCount = 0; /* check input parameters */ if (pCommand == NULL || pInput == NULL || pInput->ppTokens == NULL) { goto Finish; } if (pInput->TokenCount < 1) { NVDIMM_DBG("No input specified for Parse"); goto Finish; } /* parse the input */ Start = 0; ZeroMem(pCommand, sizeof(struct Command)); for (Index = 0; Index < MAX_TARGETS; Index++) { pCommand->targets[Index].pTargetValueStr = AllocateZeroPool(TARGET_VALUE_LEN * sizeof(CHAR16)); if (!pCommand->targets[Index].pTargetValueStr) { ReturnCode = EFI_OUT_OF_RESOURCES; break; } } for (Index = 0; Index < MAX_OPTIONS; Index++) { pCommand->options[Index].pOptionValueStr = AllocateZeroPool(PARSER_OPTION_VALUE_LEN * sizeof(CHAR16)); if (!pCommand->options[Index].pOptionValueStr) { ReturnCode = EFI_OUT_OF_RESOURCES; break; } } ReturnCode = findVerb(&Start, pInput, pCommand); if (EFI_ERROR(ReturnCode)) { goto Finish; } pHelpStr = getCommandHelp(pCommand, FALSE); ReturnCode = InvalidTokenScreen(pInput, pHelpStr); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = findOptions(&Start, pInput, pCommand); /** Catch errors and send appropriate message **/ if (EFI_ERROR(ReturnCode)) { switch (ReturnCode) { case EFI_BUFFER_TOO_SMALL: // Too long option value SetSyntaxError(CatSPrint(NULL, CLI_PARSER_ERR_INVALID_OPTION_VALUES FORMAT_NL_STR, pHelpStr)); break; } ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /* If protocol or payload size options present, ensure no mutually exclusive protocol/payload options */ ReturnCode = ValidateProtocolAndPayloadSizeOptions(pCommand); if (EFI_NOT_FOUND != ReturnCode && EFI_ERROR(ReturnCode) && FALSE == HelpRequested) { goto Finish; } ReturnCode = findTargets(&Start, pInput, pCommand); if (EFI_ERROR(ReturnCode)) { switch (ReturnCode) { case EFI_BUFFER_TOO_SMALL: SetSyntaxError(CatSPrint(NULL, CLI_PARSER_ERR_INVALID_TARGET_VALUES FORMAT_NL_STR, pHelpStr)); break; } ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = findProperties(&Start, pInput, pCommand); if (EFI_ERROR(ReturnCode)) { goto Finish; } /* check if input target ordering is correct */ if (!InputTargetsValid(pCommand->targets, &pSyntaxErrorStr)) { pCommand->ShowHelp = TRUE; SetSyntaxError(CatSPrint(NULL, FORMAT_STR_NL FORMAT_STR_NL FORMAT_STR, pSyntaxErrorStr, CLI_PARSER_DID_YOU_MEAN, pHelpStr)); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /* try to match the parsed input against a registered command */ for (Index = 0; Index < gCommandCount; Index++) { ReturnCode = MatchCommand(pCommand, &gCommandList[Index]); if (!EFI_ERROR(ReturnCode)) { pCommand->run = gCommandList[Index].run; pCommand->PrinterCtrlSupported = gCommandList[Index].PrinterCtrlSupported; pCommand->ExcludeDriverBinding = gCommandList[Index].ExcludeDriverBinding; break; } } if (EFI_ERROR(ReturnCode)) { for (Index = 0; Index < gCommandCount; Index++) { //if at least the verb matches, then set this command up for help display if (StrICmp(pCommand->verb, gCommandList[Index].verb) == 0) { pCommand->ShowHelp = TRUE; ReturnCode = EFI_SUCCESS; break; } } } /* try to give the user more useful help */ if (EFI_ERROR(ReturnCode)) { if (pCommand->ShowHelp == TRUE) { /** If user used -help option, but provided command does not match any command syntax - display syntax of any command containing verb of entered command and return EFI_SUCCESS **/ SetSyntaxError(CatSPrint(NULL, FORMAT_STR, getCommandHelp(pCommand, FALSE))); LongPrint(getSyntaxError()); ReturnCode = EFI_SUCCESS; } else if (gPossibleMatchCount == 1 && gDetailedSyntaxError) { SetSyntaxError(CatSPrint(NULL, L"Syntax Error: " FORMAT_STR_NL L"Correct syntax: " FORMAT_STR, gDetailedSyntaxError, pHelpStr)); } else { SetSyntaxError(CatSPrint(NULL, FORMAT_STR_NL FORMAT_STR_NL FORMAT_STR, CLI_PARSER_ERR_INVALID_COMMAND, CLI_PARSER_DID_YOU_MEAN, pHelpStr)); } } Finish: FREE_POOL_SAFE(pSyntaxErrorStr); FREE_POOL_SAFE(pHelpStr); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /* * Identify the verb in the input */ EFI_STATUS findVerb(UINTN *pStart, struct CommandInput *pInput, struct Command *pCommand) { EFI_STATUS rc = EFI_INVALID_PARAMETER; UINTN i = 0; NVDIMM_ENTRY(); /* there has to be at least one verb */ if (*pStart >= pInput->TokenCount) { return rc; } for (i = 0; i < gCommandCount; i++) { if (StrICmp(gCommandList[i].verb, pInput->ppTokens[*pStart]) == 0) { /* verb matches, so store it and move on */ StrnCpyS(pCommand->verb, VERB_LEN, pInput->ppTokens[*pStart], VERB_LEN - 1); (*pStart)++; rc = EFI_SUCCESS; break; } } /* more detailed error */ if (EFI_ERROR(rc)) { #ifdef OS_BUILD if (g_basic_commands) { // This should be updated when there are other commands a non-root user can run #ifdef _MSC_VER Print(L"Sorry, the ipmctl command you have attempted to execute requires admin privileges.\n"); #else //_MSC_VER Print(L"Sorry, the ipmctl command you have attempted to execute requires root privileges.\n"); #endif //_MSC_VER } else { #endif //OS_BUILD SetSyntaxError(CatSPrint(NULL, CLI_PARSER_ERR_VERB_EXPECTED, pInput->ppTokens[*pStart])); #ifdef OS_BUILD } #endif } NVDIMM_EXIT_I64(rc); return rc; } /* * Identify the options in the input */ EFI_STATUS findOptions(UINTN *pStart, struct CommandInput *pInput, struct Command *pCommand) { EFI_STATUS Rc = EFI_SUCCESS; UINTN Index = 0; UINTN Index2 = 0; UINTN Index3 = 0; UINTN matchedOptions = 0; BOOLEAN Found = FALSE; CHAR16 *pHelpStr = NULL; CHAR16 *pTmpString = NULL; NVDIMM_ENTRY(); pHelpStr = getCommandHelp(pCommand, FALSE); if (NULL == gCommandList || 0 == gCommandCount) { Rc = EFI_INVALID_PARAMETER; goto Finish; } /** loop through the input tokens **/ while ((pInput->TokenCount - *pStart) > 0) { Found = FALSE; /** loop through the supported commands to find valid options **/ for (Index = 0; Index < gCommandCount && !Found; Index++) { for (Index2 = 0; Index2 < MAX_OPTIONS && !Found; Index2++) { /** check both the long and short version of each option **/ if ((StrICmp(pInput->ppTokens[*pStart], HELP_OPTION) == 0) || (StrICmp(pInput->ppTokens[*pStart], HELP_OPTION_SHORT) == 0)) { pCommand->ShowHelp = TRUE; Found = TRUE; } else if (StrICmp(gCommandList[Index].options[Index2].OptionNameShort, pInput->ppTokens[*pStart]) == 0) { // Check if option is copied already - to prevent duplicated option for (Index3 = 0; Index3 < matchedOptions; Index3++) { if (StrICmp(pCommand->options[Index3].OptionNameShort, pInput->ppTokens[*pStart]) == 0) { pTmpString = CatSPrint(NULL, CLI_PARSER_ERR_UNEXPECTED_TOKEN, pInput->ppTokens[*pStart]); SetSyntaxError(CatSPrintClean(pTmpString, FORMAT_NL_STR FORMAT_NL_STR, CLI_PARSER_DID_YOU_MEAN, pHelpStr)); Rc = EFI_INVALID_PARAMETER; goto Finish; } } StrnCpyS(pCommand->options[matchedOptions].OptionNameShort, OPTION_LEN, pInput->ppTokens[*pStart], OPTION_LEN - 1); Found = TRUE; } else if (StrICmp(gCommandList[Index].options[Index2].OptionName, pInput->ppTokens[*pStart]) == 0) { // Check if option is copied already - to prevent duplicated option for (Index3 = 0; Index3 < matchedOptions; Index3++) { if (StrICmp(pCommand->options[Index3].OptionName, pInput->ppTokens[*pStart]) == 0) { pTmpString = CatSPrint(NULL, CLI_PARSER_ERR_UNEXPECTED_TOKEN, pInput->ppTokens[*pStart]); SetSyntaxError(CatSPrintClean(pTmpString, FORMAT_NL_STR FORMAT_NL_STR, CLI_PARSER_DID_YOU_MEAN, pHelpStr)); Rc = EFI_INVALID_PARAMETER; goto Finish; } } StrnCpyS(pCommand->options[matchedOptions].OptionName, OPTION_LEN, pInput->ppTokens[*pStart], OPTION_LEN - 1); Found = TRUE; } /** if option is found, move to the next token **/ if (Found) { (*pStart)++; /** check for an option value **/ if (((pInput->TokenCount - *pStart) >= 1) && (pInput->ppTokens[*pStart][0] != '-')) { if (StrLen(pInput->ppTokens[*pStart]) > PARSER_OPTION_VALUE_LEN) { Rc = EFI_BUFFER_TOO_SMALL; break; } else { if (pCommand->options[matchedOptions].pOptionValueStr == NULL) { pTmpString = CatSPrint(NULL, CLI_PARSER_ERR_UNEXPECTED_TOKEN, pInput->ppTokens[*pStart]); SetSyntaxError(CatSPrintClean(pTmpString, FORMAT_NL_STR FORMAT_NL_STR, CLI_PARSER_DID_YOU_MEAN, pHelpStr)); Rc = EFI_INVALID_PARAMETER; goto Finish; } StrnCpyS(pCommand->options[matchedOptions].pOptionValueStr, PARSER_OPTION_VALUE_LEN, pInput->ppTokens[*pStart], PARSER_OPTION_VALUE_LEN - 1); (*pStart)++; } } matchedOptions++; } } } /** then this is not an option so move on **/ if (!Found) { break; } } Finish: FREE_POOL_SAFE(pHelpStr) NVDIMM_EXIT_I64(Rc); return Rc; } /* * Identify the targets in the input */ EFI_STATUS findTargets(UINTN *pStart, struct CommandInput *pInput, struct Command *pCommand) { EFI_STATUS Rc = EFI_SUCCESS; UINTN Index = 0; UINTN Index2 = 0; UINTN Index3 = 0; BOOLEAN Found = FALSE; UINTN matchedTargets = 0; CHAR16 *pHelpStr = NULL; CHAR16 *pTmpStr = NULL; NVDIMM_ENTRY(); pHelpStr = getCommandHelp(pCommand, FALSE); if (NULL == gCommandList || 0 == gCommandCount) { Rc = EFI_INVALID_PARAMETER; goto Finish; } /* loop through the input tokens */ while ((pInput->TokenCount - *pStart) > 0) { Found = FALSE; /* check input against supported targets */ for (Index = 0; Index < gCommandCount && !Found; Index++) { for (Index2 = 0; Index2 < MAX_TARGETS && !Found; Index2++) { if (StrICmp(gCommandList[Index].targets[Index2].TargetName, pInput->ppTokens[*pStart]) == 0) { // Check if option is copied already - to prevent duplicated option for (Index3 = 0; Index3 < matchedTargets; Index3++) { if (StrICmp(pCommand->targets[Index3].TargetName, pInput->ppTokens[*pStart]) == 0) { pTmpStr = CatSPrint(NULL, CLI_PARSER_ERR_UNEXPECTED_TOKEN, pInput->ppTokens[*pStart]); SetSyntaxError(CatSPrintClean(pTmpStr, FORMAT_NL_STR FORMAT_NL_STR, CLI_PARSER_DID_YOU_MEAN, pHelpStr)); Rc = EFI_INVALID_PARAMETER; } } StrnCpyS(pCommand->targets[matchedTargets].TargetName, TARGET_LEN, pInput->ppTokens[*pStart], TARGET_LEN - 1); (*pStart)++; Found = TRUE; /* check for a target value */ if (((pInput->TokenCount - *pStart) >= 1) && (pInput->ppTokens[*pStart][0] != '-') && !ContainsCharacter('=', pInput->ppTokens[*pStart])) { if (StrLen(pInput->ppTokens[*pStart]) > TARGET_VALUE_LEN) { Rc = EFI_BUFFER_TOO_SMALL; break; } else { StrnCpyS(pCommand->targets[matchedTargets].pTargetValueStr, TARGET_VALUE_LEN, pInput->ppTokens[*pStart], TARGET_VALUE_LEN - 1); (*pStart)++; } } matchedTargets++; } } } /* then this is not an target so move on */ if (!Found) { break; } } Finish: FREE_POOL_SAFE(pHelpStr); NVDIMM_EXIT_I64(Rc); return Rc; } /* * Identify the properties in the input */ EFI_STATUS findProperties(UINTN *pStart, struct CommandInput *pInput, struct Command *pCommand) { EFI_STATUS Rc; CHAR16 *propertyName; CHAR16 *propertyValue; UINT16 propertyLength; UINTN matchedProperties; BOOLEAN Found; UINTN Index; UINTN Index2; CHAR16 *pHelpStr = NULL; CHAR16 *pTmpStr = NULL; NVDIMM_ENTRY(); Rc = EFI_SUCCESS; /* no properties are required so default to success */ matchedProperties = 0; pHelpStr = getCommandHelp(pCommand, FALSE); if (NULL == gCommandList || 0 == gCommandCount) { Rc = EFI_INVALID_PARAMETER; goto Finish; } /* loop through the input tokens */ while (((pInput->TokenCount - *pStart) > 0) && (EFI_SUCCESS == Rc)) { Rc = EFI_INVALID_PARAMETER; Found = FALSE; propertyName = NULL; propertyValue = NULL; /* properties follow the format key=value, so check for = */ if (StrStr(pInput->ppTokens[*pStart], L"=") != NULL) { /* split the property into name and value */ propertyLength = (UINT16)StrLen(pInput->ppTokens[*pStart]) + 1; propertyValue = AllocatePool(propertyLength * sizeof(CHAR16)); if (!propertyValue) { Rc = EFI_OUT_OF_RESOURCES; continue; } StrnCpyS(propertyValue, propertyLength, pInput->ppTokens[*pStart], propertyLength - 1); propertyName = StrTok(&propertyValue, L'='); /* name is valid */ if (propertyName) { /* * loop through each command to see if the * property name matches any supported properties */ for (Index = 0; Index < gCommandCount && !Found; Index++) { for (Index2 = 0; Index2 < MAX_PROPERTIES && !Found; Index2++) { /* found a matching property */ if (StrICmp(gCommandList[Index].properties[Index2].PropertyName, propertyName) == 0) { StrnCpyS(pCommand->properties[matchedProperties].PropertyName, PROPERTY_KEY_LEN, propertyName, PROPERTY_KEY_LEN - 1); /* value is valid */ if (StrLen(propertyValue) > 0) { StrnCpyS(pCommand->properties[matchedProperties].PropertyValue, PROPERTY_VALUE_LEN, propertyValue, PROPERTY_VALUE_LEN - 1); } Found = 1; (*pStart)++; matchedProperties++; if (matchedProperties < MAX_PROPERTIES) { Rc = EFI_SUCCESS; } else { Rc = EFI_OUT_OF_RESOURCES; } break; /* move to the next property */ } } } /* clean up */ if (propertyName) { FreePool(propertyName); } } /* no property value */ /* clean up */ if (propertyValue) { FreePool(propertyValue); } } /* no property name */ /* bad property or unexpected token */ if (!Found) { pTmpStr = CatSPrint(NULL, CLI_PARSER_ERR_UNEXPECTED_TOKEN, pInput->ppTokens[*pStart]); SetSyntaxError(CatSPrintClean(pTmpStr, FORMAT_NL_STR FORMAT_NL_STR, CLI_PARSER_DID_YOU_MEAN, pHelpStr)); Rc = EFI_INVALID_PARAMETER; continue; } } Finish: FREE_POOL_SAFE(pHelpStr); NVDIMM_EXIT_I(Rc); return Rc; } /* * Attempt to match the input to a command */ EFI_STATUS MatchCommand(struct Command *pInput, struct Command *pMatch) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_STATUS OptionsMatched = EFI_SUCCESS; EFI_STATUS TargetsMatched = EFI_SUCCESS; EFI_STATUS PropertiesMatched = EFI_SUCCESS; if (pInput == NULL || pMatch == NULL) { goto Finish; } /* match the verb */ if (StrICmp(pMatch->verb, pInput->verb) == 0) { gPossibleMatchCount++; OptionsMatched = MatchOptions(pInput, pMatch); TargetsMatched = MatchTargets(pInput, pMatch); PropertiesMatched = MatchProperties(pInput, pMatch); /* try match the options, targets and properties */ if ((OptionsMatched == EFI_SUCCESS) && (TargetsMatched == EFI_SUCCESS) && (PropertiesMatched == EFI_SUCCESS)) { /* found match! */ ReturnCode = EFI_SUCCESS; } } Finish: return ReturnCode; } /* * Attempt to match the input based on the options */ EFI_STATUS MatchOptions( IN struct Command *pInput, IN struct Command *pMatch ) { EFI_STATUS ReturnCode = EFI_SUCCESS; BOOLEAN MissingValue = FALSE; BOOLEAN RedundantValue = FALSE; UINTN MatchInputOptions = 0; UINTN MatchInputRequired = 0; UINTN MatchCommandOptions = 0; UINTN MatchCommandRequired = 0; UINTN MatchCount = 0; UINTN Index = 0; UINTN Index2 = 0; // Count options that need match in input command and matching command for (Index = 0; Index < MAX_OPTIONS; Index++) { if (StrLen(pInput->options[Index].OptionName) > 0 || StrLen(pInput->options[Index].OptionNameShort) > 0) { MatchInputOptions++; } else { break; } } for (Index = 0; Index < MAX_OPTIONS; Index++) { if (StrLen(pMatch->options[Index].OptionName) > 0 || StrLen(pMatch->options[Index].OptionNameShort) > 0) { MatchCommandOptions++; if (pMatch->options[Index].Required) { MatchCommandRequired++; } } else { break; } } if (MatchInputOptions > MAX_OPTIONS || MatchCommandOptions > MAX_OPTIONS) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_WARN("Too many options have been provided."); goto Finish; } for (Index = 0; Index < MatchInputOptions; Index++) { MissingValue = FALSE; RedundantValue = FALSE; for (Index2 = 0; Index2 < MatchCommandOptions; Index2++) { // check if option name matches if (StrICmp(pMatch->options[Index2].OptionName, pInput->options[Index].OptionName) == 0 || StrICmp(pMatch->options[Index2].OptionNameShort, pInput->options[Index].OptionNameShort) == 0) { // check if option is required if (pMatch->options[Index2].Required) { MatchInputRequired++; } // check if value is optional or required if (pMatch->options[Index2].ValueRequirement != ValueOptional) { if (NULL != pInput->options[Index].pOptionValueStr && StrLen(pInput->options[Index].pOptionValueStr) > 0) { if (pMatch->options[Index2].ValueRequirement == ValueRequired) { MatchCount++; } else { RedundantValue = TRUE; } break; } else { if (pMatch->options[Index2].ValueRequirement == ValueEmpty) { MatchCount++; } else { MissingValue = TRUE; } break; } } else { MatchCount++; break; } } } if (MatchCount <= Index) { // option specified with missing value if (MissingValue) { SetDetailedSyntaxError( CatSPrint(NULL, CLI_PARSER_DETAILED_ERR_OPTION_VALUE_REQUIRED, pMatch->options[Index2].OptionName)); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (RedundantValue) { SetDetailedSyntaxError( CatSPrint(NULL, CLI_PARSER_DETAILED_ERR_OPTION_VALUE_UNEXPECTED, pMatch->options[Index2].OptionName)); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // missing required option if (Index2 < MatchCommandOptions && pMatch->options[Index2].Required) { SetDetailedSyntaxError( CatSPrint(NULL, CLI_PARSER_DETAILED_ERR_OPTION_REQUIRED, pMatch->options[Index2].OptionName)); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; // stop looping } // if the user passed in an invalid option if (StrLen(pInput->options[Index].OptionName) > 0 && !containsOption(pMatch, pInput->options[Index].OptionName)) { SetSyntaxError(CatSPrint(NULL, CLI_PARSER_ERR_UNEXPECTED_TOKEN, pInput->options[Index].OptionName)); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; // stop looping } // if the user passed in an invalid option abbreviation if (StrLen(pInput->options[Index].OptionNameShort) > 0 && !containsOption(pMatch, pInput->options[Index].OptionNameShort)) { SetSyntaxError(CatSPrint(NULL, CLI_PARSER_ERR_UNEXPECTED_TOKEN, pInput->options[Index].OptionNameShort)); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; // stop looping } } } if (MatchCount < MatchInputOptions || MatchInputRequired < MatchCommandRequired) { ReturnCode = EFI_NOT_FOUND; } Finish: return ReturnCode; } /* Gives the total number of targets associated with a command */ UINT16 TargetCount(struct Command *pCmd) { UINT16 val = 0; UINT16 Index = 0; CHAR16 *TargetName = NULL; UINTN TargetNameLen = 0; if (pCmd == NULL) { return val; } for (Index = 0; Index < MAX_TARGETS; ++Index) { TargetName = pCmd->targets[Index].TargetName; TargetNameLen = StrLen(TargetName); if (TargetNameLen == 0) { // All targets from Input was processed, quit from loop. break; } val++; } return val; } /* Gives the total number of targets that match an input as compared to a given */ UINT16 TargetMatchCount(struct Command *pInputCmd, struct Command *pCmdToMatch) { UINT16 val = 0; UINT16 Index1 = 0; UINT16 Index2 = 0; CHAR16 *InputName = NULL; UINTN InputNameLen = 0; CHAR16 *MatchName = NULL; UINTN MatchNameLen = 0; if (pInputCmd == NULL || pCmdToMatch == NULL || StrICmp(pInputCmd->verb, pCmdToMatch->verb) != 0) { return val; } for (Index1 = 0; Index1 < MAX_TARGETS; ++Index1) { InputName = pInputCmd->targets[Index1].TargetName; InputNameLen = StrLen(InputName); if (InputNameLen == 0) { // All targets from Input was processed, quit from loop. break; } for (Index2 = 0; Index2 < MAX_TARGETS; ++Index2) { MatchName = pCmdToMatch->targets[Index2].TargetName; MatchNameLen = StrLen(MatchName); if (MatchNameLen == 0) { // All targets from Input was processed, quit from loop. break; } if (StrICmp(InputName, MatchName) == 0) { val++; } } } return val; } /** Validate input targets combination Function compares input targets with invalid sequences @param[in] pInputTargets is a pointer to the list of input command targets @param[out] ppErrorString is a pointer to a pointer to the return error message @retval TRUE if input command targets does not match any known invalid combination @retval FALSE if all targets matches to invalid combination **/ BOOLEAN InputTargetsValid(struct target *pInputTargets, CHAR16 **ppErrorString) { BOOLEAN ReturnValue = TRUE; UINT16 IndexSeq = 0; UINT16 IndexTar = 0; CHAR16 *InputName = NULL; CHAR16 *MatchName = NULL; BOOLEAN Match = FALSE; static const struct TargetsCombination InvalidSequences[] = { { {L"-socket", L"-dimm", NULL}, L"Invalid input target ordering: -socket -dimm" } }; if (pInputTargets == NULL) { goto Finish; } for (IndexSeq = 0; IndexSeq < ARRAY_SIZE(InvalidSequences); IndexSeq++) { Match = TRUE; for (IndexTar = 0; IndexTar < MAX_TARGETS; IndexTar++) { InputName = pInputTargets[IndexTar].TargetName; MatchName = InvalidSequences[IndexSeq].pTargets[IndexTar]; if (MatchName == NULL) { // Input and invalid sequence match! Quit from loop. break; } if (StrICmp(InputName, MatchName) != 0) { // Targets are different. Quit from loop. Match = FALSE; break; } } if (Match == TRUE) { *ppErrorString = CatSPrint(NULL, FORMAT_STR, InvalidSequences[IndexSeq].pErrString); ReturnValue = FALSE; break; } } Finish: return ReturnValue; } /* * Attempt to match the input based on the targets */ EFI_STATUS MatchTargets(struct Command *pInputCmd, struct Command *pCmdToMatch) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; BOOLEAN MissingTargetValue = FALSE; BOOLEAN RedundantTargetValue = FALSE; BOOLEAN MissingRequiredTarget = FALSE; BOOLEAN ExcessiveTarget = FALSE; BOOLEAN Matched = FALSE; UINT8 IndexInput = 0; UINT8 IndexMatch = 0; UINTN InputTargetNameLen = 0; UINTN InputTargetValueLen = 0; UINTN MatchTargetNameLen = 0; CHAR16 *InputTargetName = NULL; CHAR16 *MatchTargetName = NULL; UINT8 RequiredTargetsAsFlags = 0; UINT8 FoundTargetsAsFlags = 0; // First find all required targets to match for (IndexMatch = 0; IndexMatch < MAX_TARGETS; ++IndexMatch) { if (pCmdToMatch->targets[IndexMatch].Required) { RequiredTargetsAsFlags |= (1 << IndexMatch); } } // Iterate all targets from input cmd for (IndexInput = 0; IndexInput < MAX_TARGETS; ++IndexInput) { InputTargetName = pInputCmd->targets[IndexInput].TargetName; InputTargetNameLen = StrLen(InputTargetName); if (InputTargetNameLen == 0) { // All targets from Input was processed, quit from loop. break; } Matched = FALSE; // Iterate all targets from command to match for (IndexMatch = 0; IndexMatch < MAX_TARGETS; ++IndexMatch) { MatchTargetName = pCmdToMatch->targets[IndexMatch].TargetName; MatchTargetNameLen = StrLen(MatchTargetName); if (MatchTargetNameLen == 0) { // All targets from Match processed, quit from inner loop. break; } if (StrICmp(MatchTargetName, InputTargetName) == 0) { // Matching target found, turn on flag FoundTargetsAsFlags |= (1 << IndexMatch); Matched = TRUE; } else { continue; } if (pInputCmd->ShowHelp == TRUE) { continue; } // Get target value from user given cmd InputTargetValueLen = StrLen(pInputCmd->targets[IndexInput].pTargetValueStr); // Check target value if is missing or redundant: if ((pCmdToMatch->targets[IndexMatch].ValueRequirement == ValueRequired) && InputTargetValueLen == 0) { //If target value is required but empty print help for target MissingTargetValue = TRUE; SetDetailedSyntaxError( CatSPrint(NULL, CLI_PARSER_DETAILED_ERR_TARGET_VALUE_REQUIRED, pInputCmd->targets[IndexInput].TargetName)); goto Finish; } else if ((pCmdToMatch->targets[IndexMatch].ValueRequirement == ValueEmpty) && InputTargetValueLen != 0) { RedundantTargetValue = TRUE; SetDetailedSyntaxError( CatSPrint(NULL, CLI_PARSER_DETAILED_ERR_TARGET_VALUE_UNEXPECTED, pInputCmd->targets[IndexInput].TargetName)); goto Finish; } } if (Matched == FALSE){ // User target not found, command not matched. ExcessiveTarget = TRUE; goto Finish; } } // Check if all required target has been found. MissingRequiredTarget = (RequiredTargetsAsFlags & FoundTargetsAsFlags) != RequiredTargetsAsFlags; Finish: if (!MissingRequiredTarget && !ExcessiveTarget) { // All required targets matched, now check Value errors. if (MissingTargetValue || RedundantTargetValue) { ReturnCode = EFI_INVALID_PARAMETER; } else { ReturnCode = EFI_SUCCESS; // All ok, matched target } } else { //NVDIMM_WARN("Input don't match Command with ID=%d", pCmdToMatch->CommandId); ReturnCode = EFI_NOT_FOUND; } return ReturnCode; } /* * Attempt to match the input based on the properties */ EFI_STATUS MatchProperties(struct Command *pInput, struct Command *pMatch) { EFI_STATUS ReturnCode = EFI_SUCCESS; BOOLEAN Matched = TRUE; UINTN IndexInput = 0; UINTN IndexMatch = 0; BOOLEAN PropertyFound[MAX_PROPERTIES] = {FALSE}; UINT16 InputPropertyValueLen = 0; for (IndexMatch = 0; IndexMatch < MAX_PROPERTIES && StrLen(pMatch->properties[IndexMatch].PropertyName) != 0; IndexMatch++) { Matched = FALSE; for (IndexInput = 0; IndexInput < MAX_PROPERTIES && StrLen(pInput->properties[IndexInput].PropertyName) != 0; IndexInput++) { if (StrICmp(pMatch->properties[IndexMatch].PropertyName, pInput->properties[IndexInput].PropertyName) == 0) { Matched = TRUE; /* Get property value from user given cmd */ InputPropertyValueLen = (UINT16)StrLen(pInput->properties[IndexInput].PropertyValue); /* Check property value if is missing or redundant */ if ((pMatch->properties[IndexMatch].ValueRequirement == ValueRequired) && InputPropertyValueLen == 0) { SetDetailedSyntaxError( CatSPrint(NULL, CLI_PARSER_DETAILED_ERR_PROPERTY_VALUE_REQUIRED, pInput->properties[IndexInput].PropertyName)); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } else if ((pMatch->properties[IndexMatch].ValueRequirement == ValueEmpty) && InputPropertyValueLen != 0) { SetDetailedSyntaxError( CatSPrint(NULL, CLI_PARSER_DETAILED_ERR_PROPERTY_VALUE_UNEXPECTED, pInput->properties[IndexInput].PropertyName)); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } break; } } if (!Matched && pMatch->properties[IndexMatch].Required) { SetDetailedSyntaxError( CatSPrint(NULL, CLI_PARSER_DETAILED_ERR_PROPERTY_REQUIRED, pMatch->properties[IndexMatch].PropertyName)); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } /* Looking for foreign and doubled properties */ for (IndexInput = 0; IndexInput < MAX_PROPERTIES && StrLen(pInput->properties[IndexInput].PropertyName) != 0 ; IndexInput++) { Matched = FALSE; for (IndexMatch = 0; IndexMatch < MAX_PROPERTIES && StrLen(pMatch->properties[IndexMatch].PropertyName) != 0; IndexMatch++) { if ((StrICmp(pInput->properties[IndexInput].PropertyName, pMatch->properties[IndexMatch].PropertyName) == 0) && !PropertyFound[IndexMatch]) { Matched = TRUE; PropertyFound[IndexMatch] = TRUE; break; } } if (!Matched) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } Finish: return ReturnCode; } /** Get the help for a command read from the user. @param[in] pCommand a pointer to the parsed struct Command. @param[in] SingleCommand a BOOLEAN flag indicating if we are trying to match to a single command help or to all commands with the same verb. @retval NULL if the command verb could not be matched to any of the registered commands. Or the pointer to the help message. NOTE: If the return pointer is not NULL, the caller is responsible to free the memory using FreePool. **/ CHAR16 *getCommandHelp( IN struct Command *pCommand, BOOLEAN SingleCommand ) { UINTN Index = 0; UINTN Index2 = 0; UINT16 CommandsToDisplay = 0; UINT16 MatchingTargets = 0; CHAR16 *pHelp = NULL; NVDIMM_ENTRY(); //reset flags for (Index = 0; Index < gCommandCount; Index++) { gCommandList[Index].SyntaxErrorHelpNeeded = FALSE; gCommandList[Index].VerbMatch = pCommand != NULL && StrICmp(pCommand->verb, gCommandList[Index].verb) == 0; } //locate which of the commands to display help for if (TRUE == SingleCommand && pCommand != NULL) { for (Index = 0; Index < gCommandCount; Index++) { //don't bother with commands of a different verb if (FALSE == gCommandList[Index].VerbMatch) { continue; } //Exact matches are an immediate break for displaying that one command if (EFI_SUCCESS == MatchCommand(pCommand, &gCommandList[Index])) { for (Index2 = 0; Index2 < gCommandCount; Index2++) { gCommandList[Index2].SyntaxErrorHelpNeeded = FALSE; } gCommandList[Index].SyntaxErrorHelpNeeded = TRUE; CommandsToDisplay++; break; } //if the caller has sent in a target, see if there is a partial match MatchingTargets = TargetMatchCount(pCommand, &gCommandList[Index]); if (MatchingTargets > 0) { gCommandList[Index].SyntaxErrorHelpNeeded = TRUE; CommandsToDisplay++; } } //no matches = display them all if (0 == CommandsToDisplay) { for (Index = 0; Index < gCommandCount; Index++) { //don't bother with commands of a different verb if (FALSE == gCommandList[Index].VerbMatch) { continue; } gCommandList[Index].SyntaxErrorHelpNeeded = TRUE; } } } for (Index = 0; Index < gCommandCount; Index++) { /** if the user wants help for a specific command and it matches the verb, then continue to add the pHelp **/ if (!gCommandList[Index].Hidden && ((!SingleCommand && (pCommand == NULL || TRUE == gCommandList[Index].VerbMatch)))) { /** full verb syntax with help string **/ if (pCommand == NULL || (pCommand != NULL && pCommand->ShowHelp == TRUE)) { pHelp = CatSPrintClean(pHelp, L" " FORMAT_STR_NL, gCommandList[Index].pHelp); pHelp = CatSPrintClean(pHelp, L" " FORMAT_STR_SPACE, gCommandList[Index].verb); } else { /** syntax error help so just print syntax **/ pHelp = CatSPrintClean(pHelp, L" " FORMAT_STR_SPACE, gCommandList[Index].verb); } /* Only show the required fields*/ for (Index2 = 0; Index2 < MAX_OPTIONS; Index2++) { if (StrLen(gCommandList[Index].options[Index2].OptionName) > 0) { if (gCommandList[Index].options[Index2].Required) { pHelp = CatSPrintClean(pHelp, FORMAT_STR_SPACE, gCommandList[Index].options[Index2].OptionName); if (StrLen(gCommandList[Index].options[Index2].pHelp) != 0) { pHelp = CatSPrintClean(pHelp, L"(" FORMAT_STR_SPACE L")", gCommandList[Index].options[Index2].pHelp); } } } } if (StrICmp(gCommandList[Index].verb, LOAD_VERB) == 0) { pHelp = CatSPrintClean(pHelp, L"-source (filename) "); } else if (StrICmp(gCommandList[Index].verb, DUMP_VERB) == 0) { pHelp = CatSPrintClean(pHelp, L"-destination (filename) "); } /* add the targets pHelp */ for (Index2 = 0; Index2 < MAX_TARGETS; Index2++) { if (StrLen(gCommandList[Index].targets[Index2].TargetName) > 0) { if (!gCommandList[Index].targets[Index2].Required) { pHelp = CatSPrintClean(pHelp, L"["); } pHelp = CatSPrintClean(pHelp, FORMAT_STR_SPACE, gCommandList[Index].targets[Index2].TargetName); if (NULL != gCommandList[Index].targets[Index2].pHelp && StrLen(gCommandList[Index].targets[Index2].pHelp) > 0) { if (gCommandList[Index].targets[Index2].ValueRequirement == ValueOptional) { pHelp = CatSPrintClean(pHelp, L"[" FORMAT_STR L"]", gCommandList[Index].targets[Index2].pHelp); } else { pHelp = CatSPrintClean(pHelp, L"(" FORMAT_STR L")", gCommandList[Index].targets[Index2].pHelp); } } if (!gCommandList[Index].targets[Index2].Required) { pHelp = CatSPrintClean(pHelp, L"]"); } pHelp = CatSPrintClean(pHelp, L" "); } } /* only show the properties that are required */ for (Index2 = 0; Index2 < MAX_PROPERTIES; Index2++) { if(gCommandList[Index].properties[Index2].Required){ if (StrLen(gCommandList[Index].properties[Index2].PropertyName) > 0) { pHelp = CatSPrintClean(pHelp, L" "); pHelp = CatSPrintClean(pHelp, FORMAT_STR L"=(" FORMAT_STR L")", gCommandList[Index].properties[Index2].PropertyName, gCommandList[Index].properties[Index2].pHelp); } } } pHelp = CatSPrintClean(pHelp, L"\n\n"); } else if (gCommandList[Index].SyntaxErrorHelpNeeded) { /** full verb syntax with help string **/ if (NULL == pCommand || TRUE == SingleCommand || TRUE == pCommand->ShowHelp) { pHelp = CatSPrintClean(pHelp, L" " FORMAT_STR_NL, gCommandList[Index].pHelp); pHelp = CatSPrintClean(pHelp, L" " FORMAT_STR_SPACE, gCommandList[Index].verb); } else { /** syntax error help so just print syntax **/ pHelp = CatSPrintClean(pHelp, L" " FORMAT_STR_SPACE, gCommandList[Index].verb); } /* add the targets pHelp */ pHelp = CatSPrintClean(pHelp, L" [OPTIONS]"); /* Source and Destination are required for load and dump commands*/ if (StrICmp(gCommandList[Index].verb, LOAD_VERB) == 0) { pHelp = CatSPrintClean(pHelp, L"-source (filename) "); } else if (StrICmp(gCommandList[Index].verb, DUMP_VERB) == 0) { pHelp = CatSPrintClean(pHelp, L"-destination (filename) "); } for (Index2 = 0; Index2 < MAX_TARGETS; Index2++) { if (StrLen(gCommandList[Index].targets[Index2].TargetName) > 0) { if (!gCommandList[Index].targets[Index2].Required) { pHelp = CatSPrintClean(pHelp, L"["); } pHelp = CatSPrintClean(pHelp, L" "FORMAT_STR, gCommandList[Index].targets[Index2].TargetName); if (NULL != gCommandList[Index].targets[Index2].pHelp && StrLen(gCommandList[Index].targets[Index2].pHelp) > 0) { if (gCommandList[Index].targets[Index2].ValueRequirement == ValueOptional) { pHelp = CatSPrintClean(pHelp, L"[" FORMAT_STR L"]", gCommandList[Index].targets[Index2].pHelp); } else { pHelp = CatSPrintClean(pHelp, L" (" FORMAT_STR L")", gCommandList[Index].targets[Index2].pHelp); } } if (!gCommandList[Index].targets[Index2].Required) { pHelp = CatSPrintClean(pHelp, L"]"); } pHelp = CatSPrintClean(pHelp, L" "); } } if (StrLen(gCommandList[Index].properties[0].PropertyName) > 0) { pHelp = CatSPrintClean(pHelp, L"[PROPERTIES ...]"); } /* add the options pHelp */ pHelp = CatSPrintClean(pHelp, L"\n\n[OPTIONS]"); pHelp = CatSPrintClean(pHelp, L"\n [-help|-h] : Display Help for the command"); for (Index2 = 0; Index2 < MAX_OPTIONS; Index2++) { if (StrLen(gCommandList[Index].options[Index2].OptionName) > 0) { if (!gCommandList[Index].options[Index2].Required) { pHelp = CatSPrintClean(pHelp, L"\n ["); } if (StrLen(gCommandList[Index].options[Index2].OptionNameShort) > 0) { pHelp = CatSPrintClean(pHelp, FORMAT_STR L"|" FORMAT_STR, gCommandList[Index].options[Index2].OptionName, gCommandList[Index].options[Index2].OptionNameShort); } else { pHelp = CatSPrintClean(pHelp, FORMAT_STR, gCommandList[Index].options[Index2].OptionName); } if (StrLen(gCommandList[Index].options[Index2].pHelp) != 0) { pHelp = CatSPrintClean(pHelp, L"(" FORMAT_STR L")", gCommandList[Index].options[Index2].pHelp); } if (!gCommandList[Index].options[Index2].Required) { pHelp = CatSPrintClean(pHelp, L"] : "); pHelp = CatSPrintClean(pHelp, FORMAT_STR, gCommandList[Index].options[Index2].pHelpDetails); } } } pHelp = CatSPrintClean(pHelp, L"\n"); /** add the properties pHelp **/ if (StrLen(gCommandList[Index].properties[0].PropertyName) > 0 ) { pHelp = CatSPrintClean(pHelp, L"\n[PROPERTIES]"); } for (Index2 = 0; Index2 < MAX_PROPERTIES; Index2++) { if (StrLen(gCommandList[Index].properties[Index2].PropertyName) > 0) { pHelp = CatSPrintClean(pHelp, L"\n "); pHelp = CatSPrintClean(pHelp, FORMAT_STR L"=(" FORMAT_STR L")", gCommandList[Index].properties[Index2].PropertyName, gCommandList[Index].properties[Index2].pHelp); } pHelp = CatSPrintClean(pHelp, L" "); } pHelp = CatSPrintClean(pHelp, L"\n\n"); } } NVDIMM_EXIT(); return pHelp; } /** Get the overall Help for User. @retval NULL if the command verb could not be matched to any of the registered commands. Or the pointer to the help message. NOTE: If the return pointer is not NULL, the caller is responsible to free the memory using FreePool. **/ CHAR16 *getOverallCommandHelp() { UINTN Index = 0; UINTN Index2 = 0; CHAR16 *pHelp = NULL; NVDIMM_ENTRY(); //check if the page break option exists for (Index = 0; Index < gCommandCount; Index++) { /** Showing user Help for all the commands This will be simplified not showing any command description **/ /** full verb syntax **/ pHelp = CatSPrintClean(pHelp, L" " FORMAT_STR_NL, gCommandList[Index].pHelp); pHelp = CatSPrintClean(pHelp, L" " FORMAT_STR_SPACE, gCommandList[Index].verb); /* Only show the required fields for OPTIONS*/ for (Index2 = 0; Index2 < MAX_OPTIONS; Index2++) { if (StrLen(gCommandList[Index].options[Index2].OptionName) > 0) { if (gCommandList[Index].options[Index2].Required) { pHelp = CatSPrintClean(pHelp, FORMAT_STR_SPACE, gCommandList[Index].options[Index2].OptionName); if (StrLen(gCommandList[Index].options[Index2].pHelp) != 0) { pHelp = CatSPrintClean(pHelp, L"(" FORMAT_STR_SPACE L")", gCommandList[Index].options[Index2].pHelp); } } } } /* add the Targets to pHelp */ if (gCommandList[Index].targets > 0) { if (StrICmp(gCommandList[Index].verb, LOAD_VERB) == 0) { pHelp = CatSPrintClean(pHelp, L"-source (filename) "); } else if (StrICmp(gCommandList[Index].verb, DUMP_VERB) == 0) { pHelp = CatSPrintClean(pHelp, L"-destination (filename) "); } for (Index2 = 0; Index2 < MAX_TARGETS; Index2++) { if (StrLen(gCommandList[Index].targets[Index2].TargetName)> 0) { pHelp = CatSPrintClean(pHelp, FORMAT_STR, gCommandList[Index].targets[Index2].TargetName); if (NULL != gCommandList[Index].targets[Index2].pHelp && StrLen(gCommandList[Index].targets[Index2].pHelp) > 0) { if (gCommandList[Index].targets[Index2].ValueRequirement == ValueOptional) { pHelp = CatSPrintClean(pHelp, L"[" FORMAT_STR L"]", gCommandList[Index].targets[Index2].pHelp); } else { pHelp = CatSPrintClean(pHelp, L"(" FORMAT_STR L")", gCommandList[Index].targets[Index2].pHelp); } } pHelp = CatSPrintClean(pHelp, L" "); } } } /* only show PROPERTIES that are required */ for (Index2 = 0; Index2 < MAX_PROPERTIES; Index2++) { if (gCommandList[Index].properties[Index2].Required) { if (StrLen(gCommandList[Index].properties[Index2].PropertyName) > 0) { pHelp = CatSPrintClean(pHelp, L" "); pHelp = CatSPrintClean(pHelp, FORMAT_STR L"=(" FORMAT_STR L")", gCommandList[Index].properties[Index2].PropertyName, gCommandList[Index].properties[Index2].pHelp); } } } pHelp = CatSPrintClean(pHelp, L"\n\n"); } pHelp = CatSPrintClean(pHelp, L" Please see ipmctl -help i.e 'ipmctl show -help -dimm' for more information on specific command \n"); NVDIMM_EXIT(); return pHelp; } /** Check if a specific property is found @param[in] pCmd is a pointer to the struct Command that contains the user input. @param[in] pProperty is a CHAR16 string that represents the property we want to find. @retval EFI_SUCCESS if we've found the property. @retval EFI_NOT_FOUND if no such property exists for the given pCmd. @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS ContainsProperty( IN CONST struct Command *pCmd, IN CONST CHAR16 *pProperty ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; INT32 Index; NVDIMM_ENTRY(); if (pCmd == NULL || pProperty == NULL) { return EFI_INVALID_PARAMETER; } for (Index = 0; Index < MAX_PROPERTIES; Index++) { if (StrICmp(pCmd->properties[Index].PropertyName, pProperty) == 0) { ReturnCode = EFI_SUCCESS; break; } } NVDIMM_EXIT(); return ReturnCode; } /** Get a specific property value @param[in] pCmd is a pointer to the struct Command that . @param[in] pProperty is a CHAR16 string that represents the property we want to find. @param[out] ppReturnValue is a pointer to a pointer to the 16-bit character string that will contain the return property value. @retval EFI_SUCCESS if we've found the property and the value is set. @retval EFI_NOT_FOUND if no such property exists for the given pCmd. @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS GetPropertyValue( IN CONST struct Command *pCmd, IN CONST CHAR16 *pProperty, OUT CHAR16 **ppReturnValue ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; INT32 Index; NVDIMM_ENTRY(); if (pCmd == NULL || pProperty == NULL || ppReturnValue == NULL) { return EFI_INVALID_PARAMETER; } *ppReturnValue = NULL; for (Index = 0; Index < MAX_PROPERTIES; Index++) { if (StrICmp(pCmd->properties[Index].PropertyName, pProperty) == 0) { ReturnCode = EFI_SUCCESS; *ppReturnValue = (CHAR16*)pCmd->properties[Index].PropertyValue; break; } } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get the number of properties @param[in] pCmd is a pointer to the struct Command that contains the user input. @param[out] pPropertyCount represents the number of properties defined on the command line @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. @retval EFI_SUCCESS **/ EFI_STATUS GetPropertyCount( IN CONST struct Command *pCmd, IN UINT16 *pPropertyCount ) { EFI_STATUS ReturnCode = EFI_SUCCESS; INT32 Index; NVDIMM_ENTRY(); if (pCmd == NULL || pPropertyCount == NULL) { return EFI_INVALID_PARAMETER; } *pPropertyCount = 0; for (Index = 0; Index < MAX_PROPERTIES; Index++) { if (pCmd->properties[Index].PropertyName[0] != 0) { *pPropertyCount += 1; } } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /* * Check if a specific option is found */ BOOLEAN containsOption(CONST struct Command *pCmd, CONST CHAR16 *option) { BOOLEAN found = FALSE; INT32 i; NVDIMM_ENTRY(); for (i = 0; i < MAX_OPTIONS; i++) { if (StrICmp(pCmd->options[i].OptionName, option) == 0 || StrICmp(pCmd->options[i].OptionNameShort, option) == 0) { found = TRUE; break; } } NVDIMM_EXIT(); return found; } /** Check if a specific target is found in the command @param[in] pCmd @param[in] pTarget @retval TRUE if the target has been found @retval FALSE if the target has not been found **/ BOOLEAN ContainTarget( IN CONST struct Command *pCmd, IN CONST CHAR16 *pTarget ) { BOOLEAN Found = FALSE; INT32 Index; NVDIMM_ENTRY(); if (pCmd == NULL || pTarget == NULL) { return Found; } for (Index = 0; Index < MAX_TARGETS; Index++) { if (StrICmp(pCmd->targets[Index].TargetName, pTarget) == 0) { Found = TRUE; break; } } NVDIMM_EXIT(); return Found; } /* * Get the value of a specific option */ CHAR16* getOptionValue(CONST struct Command *pCmd, CONST CHAR16 *option) { INT32 i; CHAR16 *value = NULL; NVDIMM_ENTRY(); for (i = 0; i < MAX_OPTIONS; i++) { if (StrICmp(pCmd->options[i].OptionName, option) == 0 || StrICmp(pCmd->options[i].OptionNameShort, option) == 0) { value = CatSPrint(NULL, FORMAT_STR, pCmd->options[i].pOptionValueStr); break; } } NVDIMM_EXIT(); return value; } /** Get the value of a specific target @param[in] pCmd @param[in] pTarget @retval the target value if the target has been found @retval NULL otherwise **/ CHAR16* GetTargetValue( IN struct Command *pCmd, IN CONST CHAR16 *pTarget ) { INT32 Index; CHAR16 *pValue = NULL; NVDIMM_ENTRY(); if (pCmd == NULL || pTarget == NULL) { return pValue; } for (Index = 0; Index < MAX_TARGETS; Index++) { if (StrICmp(pCmd->targets[Index].TargetName, pTarget) == 0) { pValue = pCmd->targets[Index].pTargetValueStr; break; } } NVDIMM_EXIT(); return pValue; } /* * Determine if the specified value is in the specified comma * separated display list. */ BOOLEAN ContainsValue(CONST CHAR16 *displayList, CONST CHAR16 *value) { CHAR16 *tmpList; CHAR16 *token; BOOLEAN found = FALSE; NVDIMM_ENTRY(); /* copy the input to a tmp var to avoid changing it */ tmpList = CatSPrint(NULL, FORMAT_STR, displayList); if (tmpList) { token = StrTok(&tmpList, L','); while (token && !found) { if (StrICmp(value, token) == 0) { found = TRUE; } FreePool(token); token = StrTok(&tmpList, L','); } if (token) { FreePool(token); } if (tmpList) { FreePool(tmpList); } } NVDIMM_EXIT(); return found; } BOOLEAN ContainsCharacter( IN CHAR16 Character, IN CONST CHAR16* pInputString ) { UINT32 Length = 0; UINT32 Index = 0; if (pInputString == NULL) { return FALSE; } Length = (UINT32)StrLen(pInputString); for (Index=0; IndexpPrintCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_OPTION_UNITS); goto Finish; } } else { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } Finish: FREE_POOL_SAFE(pOptionsValue); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Sets a display information needed when outputting alternative formats like XML. @param[in] pName is a CHAR16 string that represents the output message. @param[in] Type represents the type of output being displayed. @param[in] pDelims is a CHAR16 string that represents delimiters to use when parsing text output @retval EFI_SUCCESS if the name was copied correctly. @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS SetDisplayInfo( IN CONST CHAR16 *pName, IN CONST UINT8 Type, IN CONST CHAR16 *pDelims ) { if (NULL == pName){ return EFI_INVALID_PARAMETER; } UnicodeSPrint(gDisplayInfo.Name, sizeof(gDisplayInfo.Name), FORMAT_STR, pName); gDisplayInfo.Type = Type; if(pDelims) { UnicodeSPrint(gDisplayInfo.Delims, sizeof(gDisplayInfo.Delims), FORMAT_STR, pDelims); } else { UnicodeSPrint(gDisplayInfo.Delims, sizeof(gDisplayInfo.Delims), L""); } return EFI_SUCCESS; } /** Get display information needed when outputting alternative formats like XML. @param[out] pName is a CHAR16 string that represents the output message. @param[int] NameSize is the size of pName in bytes @param[out] pType represents the type of output being displayed. @param[out] pDelims represents the delimiters to use when parsing text output. @param[int] DelimsSize is the size of pDelims in bytes @retval EFI_SUCCESS if the name was copied correctly. @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS GetDisplayInfo( OUT CHAR16 *pName, IN CONST UINT32 NameSize, OUT UINT8 *pType, OUT CHAR16 *pDelims, IN CONST UINT32 DelimsSize ) { if (NULL == pName || NULL == pType || NULL == pDelims){ return EFI_INVALID_PARAMETER; } UnicodeSPrint(pName, NameSize, FORMAT_STR, gDisplayInfo.Name); UnicodeSPrint(pDelims, DelimsSize, FORMAT_STR, gDisplayInfo.Delims); *pType = gDisplayInfo.Type; return EFI_SUCCESS; } /** Execute UpdateCmdCtx (if defined), run, and RunCleanup (if defined). @param[in] pCommand pointer to the command structure @retval EFI_SUCCESS if the name was copied correctly. @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS ExecuteCmd(COMMAND *pCommand) { EFI_STATUS Rc = EFI_SUCCESS; BOOLEAN CreatedPrintCtx = FALSE; if (NULL == pCommand) return EFI_INVALID_PARAMETER; //Here to support migration path from legacy print handling and new printer module if (pCommand->PrinterCtrlSupported) { // create Printer Context if not given one to use if (pCommand->pPrintCtx == NULL) { CreatedPrintCtx = TRUE; if (EFI_SUCCESS != (Rc = PrinterCreateCtx(&pCommand->pPrintCtx))) { return Rc; } if (EFI_SUCCESS != (Rc = ReadCmdLinePrintOptions(&pCommand->pPrintCtx->FormatType, pCommand))) { goto Finish; } } } else { //ensure printer ctx ptr is NULL pCommand->pPrintCtx = NULL; } if (NULL == pCommand->run) { Rc = EFI_INVALID_PARAMETER; goto Finish; } Rc = pCommand->run(pCommand); if (EFI_ERROR(Rc)) goto Finish; Finish: // clean up Printer context only if created in this routine call if (CreatedPrintCtx == TRUE) { PrinterDestroyCtx(pCommand->pPrintCtx); } return Rc; } EFI_STATUS ValidateProtocolAndPayloadSizeOptions(struct Command *pCmd) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS Attribs; if (NULL == pCmd) { NVDIMM_CRIT("NULL input parameter.\n"); goto Finish; } ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (containsOption(pCmd, PROTOCOL_OPTION_DDRT) && containsOption(pCmd, PROTOCOL_OPTION_SMBUS)) { ReturnCode = EFI_INVALID_PARAMETER; SetSyntaxError(CatSPrint(NULL, CLI_PARSER_ERR_MUTUALLY_EXCLUSIVE_OPTIONS, PROTOCOL_OPTION_DDRT, PROTOCOL_OPTION_SMBUS)); goto Finish; } else if (containsOption(pCmd, LARGE_PAYLOAD_OPTION) && containsOption(pCmd, SMALL_PAYLOAD_OPTION)) { ReturnCode = EFI_INVALID_PARAMETER; SetSyntaxError(CatSPrint(NULL, CLI_PARSER_ERR_MUTUALLY_EXCLUSIVE_OPTIONS, LARGE_PAYLOAD_OPTION, SMALL_PAYLOAD_OPTION)); goto Finish; } else if (containsOption(pCmd, PROTOCOL_OPTION_SMBUS) && containsOption(pCmd, LARGE_PAYLOAD_OPTION)) { ReturnCode = EFI_INVALID_PARAMETER; SetSyntaxError(CatSPrint(NULL, CLI_PARSER_ERR_MUTUALLY_EXCLUSIVE_OPTIONS, PROTOCOL_OPTION_SMBUS, LARGE_PAYLOAD_OPTION)); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetFisTransportAttributes(pNvmDimmConfigProtocol, &Attribs); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (containsOption(pCmd, PROTOCOL_OPTION_DDRT)) { Attribs.Protocol = FisTransportDdrt; } else if (containsOption(pCmd, PROTOCOL_OPTION_SMBUS)) { // smbus requires small payload size Attribs.Protocol = FisTransportSmbus; Attribs.PayloadSize = FisTransportSizeSmallMb; } if (containsOption(pCmd, LARGE_PAYLOAD_OPTION)) { // large payload implies ddrt (for now) Attribs.Protocol = FisTransportDdrt; Attribs.PayloadSize = FisTransportSizeLargeMb; } else if (containsOption(pCmd, SMALL_PAYLOAD_OPTION)) { Attribs.PayloadSize = FisTransportSizeSmallMb; } ReturnCode = pNvmDimmConfigProtocol->SetFisTransportAttributes(pNvmDimmConfigProtocol, Attribs); if (EFI_ERROR(ReturnCode)) { goto Finish; } Finish: if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("ValidateProtocolAndPayloadSizeOptions has returned error. Code " FORMAT_EFI_STATUS "\n", ReturnCode); } return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/CommandParser.h000066400000000000000000001110311440615110200206100ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _COMMAND_PARSER_H_ #define _COMMAND_PARSER_H_ #include #include #include #include #include #define DISP_NAME_LEN 32 //!< Display string length (used when formatting output in alternative formats) #define DISP_DELIMS_LEN 10 //!< Delimiter string length (used when formatting output in alternative formats) #define VERB_LEN 16 //!< Verb string length #define TARGET_LEN 32 //!< Target name string length #define TARGET_VALUE_LEN 4096 //!< Target value string length for maximum-possible DIMM IDs #define OPTION_LEN 16 //!< Option name string length #define OPTION_VALUE_LEN 1024 //!< Option value string length #define PARSER_OPTION_VALUE_LEN 2048 //!< Option value string length for command parser #define PROPERTY_KEY_LEN 128 //!< Property name string length #define PROPERTY_VALUE_LEN 128 //!< Property value string length #define MAX_TARGETS 8 //!< Maximum number of targets in a single command #define MAX_OPTIONS 12 //!< Maximum number of options in a single command #define MAX_PROPERTIES 20 //!< Maximum number of properties in a single command #define MAX_TOKENS 50 //!< Maximum number of tokens per line /** command keywords **/ #define LOAD_VERB L"load" #define HELP_VERB L"help" #define VERSION_VERB L"version" #define SHOW_VERB L"show" #define SET_VERB L"set" #define DELETE_VERB L"delete" #define CREATE_VERB L"create" #define DUMP_VERB L"dump" #define START_VERB L"start" #define STOP_VERB L"stop" /** command options **/ #define ALL_OPTION L"-all" //!< 'all' option name #define ALL_OPTION_SHORT L"-a" //!< 'all' option short form #define ALL_OPTION_HELP L"Show all attributes" //!< 'all' option help text #define DISPLAY_OPTION L"-display" //!< 'display' option name #define DISPLAY_OPTION_SHORT L"-d" //!< 'display' option short form #define DISPLAY_OPTION_HELP L"Show specific attributes" //!< 'display' option help text #define HELP_OPTION L"-help" //!< 'help' option name #define HELP_OPTION_SHORT L"-h" //!< 'help' option short form #define SOURCE_OPTION L"-source" //!< 'source' option name #define SOURCE_OPTION_HELP L"path" //!< 'source' option help text #define DESTINATION_OPTION L"-destination" #define DESTINATION_OPTION_HELP L"file" #define DESTINATION_PREFIX_OPTION L"-destination" #define DESTINATION_PREFIX_OPTION_HELP L"file_prefix (prefix for output files)" #define DICTIONARY_OPTION L"-dict" #define DICTIONARY_OPTION_HELP L"file" #define EXAMINE_OPTION L"-examine" //!< 'examine' option name #define EXAMINE_OPTION_SHORT L"-x" //!< 'examine' option short form #define EXAMINE_OPTION_DETAILS_TEXT L"Test the provided firmware image for compatibility" //!< 'examine' option detailed help text #define FORCE_OPTION L"-force" //!< 'force' option name #define FORCE_OPTION_SHORT L"-f" //!< 'force' option short form #define FORCE_OPTION_DETAILS_TEXT L"Suppress confirmations" //!< 'force' option help text detail #define RECOVER_OPTION L"-recover" //!< 'recover' option name #define RECOVER_OPTION_DETAILS_TEXT L"Run update on non-functional " PMEM_MODULES_STR L" only (deprecated)" //!< 'recover' option help text #define UNITS_OPTION L"-units" //!< 'units' option name #define UNITS_OPTION_B L"B" //!< 'units' option value for B #define UNITS_OPTION_MB L"MB" //!< 'units' option value for MB #define UNITS_OPTION_MIB L"MiB" //!< 'units' option value for MiB #define UNITS_OPTION_GB L"GB" //!< 'units' option value for GB #define UNITS_OPTION_GIB L"GiB" //!< 'units' option value for GiB #define UNITS_OPTION_TB L"TB" //!< 'units' option value for TB #define UNITS_OPTION_TIB L"TiB" //!< 'units' option value for TiB #define UNITS_OPTION_HELP L"B|MB|MiB|GB|GiB|TB|TiB" //!< 'units' option help text #define UNITS_OPTION_SHORT L"-u" //!< 'units' option short form #define PROPERTY_VALUE_0_1_HELP L"0|1" //!< Property 0 or 1 value #define PROPERTY_VALUE_NO_YES_IGN_HELP L"No|Yes|Ignore" //!< Property: No, Yes or Ignore #define OUTPUT_OPTION_SHORT L"-o" //!< 'output' option name short form #define OUTPUT_OPTION L"-output" //!< 'output' option #define OUTPUT_OPTION_TEXT L"text" //!< 'output' option value for text #define OUTPUT_OPTION_NVMXML L"nvmxml" //!< 'output' option value for nvmxml #define OUTPUT_OPTION_ESX_XML L"esx" //!< 'output' option value for esx xml #define OUTPUT_OPTION_ESX_TABLE_XML L"esxtable" //!< 'output' option value for esx xml #define OUTPUT_OPTION_HELP L"text|nvmxml" //!< 'output' option help text #define VERBOSE_OPTION_SHORT L"-v" //!< 'verbose' option short form #define VERBOSE_OPTION L"-verbose" //!< 'verbose' option name #define MASTER_OPTION L"-master" //!< 'master' option name #define DEFAULT_OPTION L"-default" //!< 'default' option name #define PBR_MODE_OPTION L"-mode" //!< 'mode' option name #define PROTOCOL_OPTION_DDRT L"-ddrt" //!< 'ddrt' option name #define PROTOCOL_OPTION_SMBUS L"-smbus" //!< 'smbus' option name #define LARGE_PAYLOAD_OPTION L"-lpmb" //!< 'large payload mailbox' option name #define SMALL_PAYLOAD_OPTION L"-spmb" //!< 'small payload mailbox' option name #define NFIT_OPTION L"-nfit" //!< 'nfit' option name /** command targets **/ #define DIMM_TARGET L"-dimm" //!< 'dimm' target name #define REGION_TARGET L"-region" //!< 'region' target name #define MEMORY_RESOURCES_TARGET L"-memoryresources" //!< 'memoryresources' target name #define SYSTEM_TARGET L"-system" //!< 'system' target name #define CAPABILITIES_TARGET L"-capabilities" //!< 'capabilities' target name #define SOCKET_TARGET L"-socket" //!< 'socket' target name #define GOAL_TARGET L"-goal" //!< 'goal' target name #define CAP_TARGET L"-cap" //!< 'cap' target name #define NAMESPACE_TARGET L"-namespace" //!< 'namespace' target name #define HOST_TARGET L"-host" //!< 'host' target name #define TOPOLOGY_TARGET L"-topology" //!< 'topology' target name #define CONFIG_TARGET L"-config" //!< 'config' target name #define SENSOR_TARGET L"-sensor" //!< 'sensor' target name #define ERROR_TARGET L"-error" //!< 'error' target name #define CEL_TARGET L"-cel" //!< 'cel' target name #define DEBUG_TARGET L"-debug" //!< 'debug' target name #define REGISTER_TARGET L"-register" //!< 'register' target name #define FIRMWARE_TARGET L"-firmware" //!< 'firmware' target name #define PCD_TARGET L"-pcd" //!< 'pcd' target name #define SMBIOS_TARGET L"-smbios" ///< 'smbios' target name #define SUPPORT_TARGET L"-support" //!< 'support' target name #define CONTROLLER_TEMPERATURE_TARGET_VALUE L"ControllerTemperature" //!< 'sensor' target value #define MEDIA_TEMPERATURE_TARGET_VALUE L"MediaTemperature" //!< 'sensor' target value #define SPARE_CAPACITY_TARGET_VALUE L"PercentageRemaining" //!< 'sensor' target value #define SENSOR_TARGETS \ L"MediaTemperature|ControllerTemperature|PercentageRemaining" //!< the sensors combined for the target message #define DIAGNOSTIC_TARGET L"-diagnostic" //!< 'diagnostic' target name #define ALL_TEST_TARGET_VALUE L"All" //!< 'diagnostic' target value #define QUICK_TEST_TARGET_VALUE L"Quick" //!< 'diagnostic' target value #define CONFIG_TEST_TARGET_VALUE L"Config" //!< 'diagnostic' target value #define SECURITY_TEST_TARGET_VALUE L"Security" //!< 'diagnostic' target value #define FW_TEST_TARGET_VALUE L"FW" //!< 'diagnostic' target value #define ERROR_TARGET_THERMAL_VALUE L"Thermal" //!< 'error' target value #define ERROR_TARGET_MEDIA_VALUE L"Media" //!< 'error' target value #define ALL_DIAGNOSTICS_TARGETS L"Quick|Config|Security|FW" //!< diagnostics targets combined #define PCD_CONFIG_TARGET_VALUE L"Config" #define PCD_LSA_TARGET_VALUE L"LSA" #define NFIT_TARGET_VALUE L"NFIT" //!< 'system' target value #define PCAT_TARGET_VALUE L"PCAT" //!< 'system' target value #define PMTT_TARGET_VALUE L"PMTT" //!< 'system' target value #define SYSTEM_ACPI_TARGETS \ L"NFIT|PCAT|PMTT" //!< the system acpi combined #define SMBIOS_TARGET_VALUES L"17" ///< 'smbios' target values #define FORMAT_TARGET L"-format" //!< 'format' target value #define PREFERENCES_TARGET L"-preferences" //!< 'preferences' target value #define PERFORMANCE_TARGET L"-performance" //!< 'performance' target value #define SESSION_TARGET L"-session" //!< 'session' target value #define PBR_MODE_TARGET L"-mode" //!< 'mode' target value #define PBR_RECORD_MODE_VAL L"record" //!< 'mode' target value #define PBR_PLAYBACK_MODE_VAL L"playback" //!< 'mode' target value #define PBR_PLAYBACK_MANUAL_MODE_VAL L"playback_manual" //!< 'mode' target value #define PBR_MODE_TAG L"-tag" //!< 'tag' target value /** Persistent memory type **/ #define PERSISTENT_MEM_TYPE_AD_STR L"AppDirect" #define PERSISTENT_MEM_TYPE_AD_NI_STR L"AppDirectNotInterleaved" /** command properties **/ #define TYPE_PROPERTY L"Type" //!< 'Type' property name #define TYPE_VALUE_FW L"Fw" //!< 'Type' property FW value #define TYPE_VALUE_TRAINING L"Training" //!< 'Type' property Training value #define UPDATE_PROPERTY L"Update" //!< 'Update' property name #define EXEC_PROPERTY L"Execute" //!< 'Exec' property name #define TEMPERATURE_INJ_PROPERTY L"Temperature" //!< Inject error 'Temperature' property name #define POISON_INJ_PROPERTY L"Poison" //!< Inject error 'Poison' property #define POISON_TYPE_INJ_PROPERTY L"PoisonType" //!< Inject error 'PoisonType' property #define CLEAR_ERROR_INJ_PROPERTY L"Clear" //!< Clear error injection property #define PACKAGE_SPARING_INJ_PROPERTY L"PackageSparing" //!< PackageSparing error injection property #define PERCENTAGE_REMAINING_INJ_PROPERTY L"PercentageRemaining" //!< PercentageRemaining error injection property #define FATAL_MEDIA_ERROR_INJ_PROPERTY L"FatalMediaError" //!< FatalMediaError error injection property #define DIRTY_SHUTDOWN_ERROR_INJ_PROPERTY L"DirtyShutdown" //!< DirtyShutdown error injection property #define LOCKSTATE_PROPERTY L"LockState" //!< 'LockState' property name #define LOCKSTATE_VALUE_ENABLED L"Enabled" //!< 'LockState' property Enabled value #define LOCKSTATE_VALUE_DISABLED L"Disabled" //!< 'LockState' property Disabled value #define LOCKSTATE_VALUE_UNLOCKED L"Unlocked" //!< 'LockState' property Unlocked value #define LOCKSTATE_VALUE_FROZEN L"Frozen" //!< 'LockState' property Frozen value #define CONFIG_STATUS_VALUE_VALID L"Valid" //!< 'ConfigStatus' property Valid value #define CONFIG_STATUS_VALUE_NOT_CONFIG L"Not configured" //!< 'ConfigStatus' property Not Configured value #define CONFIG_STATUS_VALUE_BAD_CONFIG L"Failed - Bad configuration" //!< 'ConfigStatus' property Bad Configuration value #define CONFIG_STATUS_VALUE_BROKEN_INTERLEAVE L"Failed - Broken interleave" //!< 'ConfigStatus' property Broken Interleave value #define CONFIG_STATUS_VALUE_REVERTED L"Failed - Reverted" //!< 'ConfigStatus' property Reverted value #define CONFIG_STATUS_VALUE_UNSUPPORTED L"Failed - Unsupported" //!< 'ConfigStatus' property Unsupported value #define CONFIG_STATUS_VALUE_PARTIALLY_SUPPORTED L"Failed - Partially supported" //!< 'ConfigStatus' property Partially Supported value #define PASSPHRASE_PROPERTY L"Passphrase" //!< 'Passphrase' property name #define NEWPASSPHRASE_PROPERTY L"NewPassphrase" //!< 'NewPassphrase' property name #define CONFIRMPASSPHRASE_PROPERTY L"ConfirmPassphrase" //!< 'ConfirmPassphrase' property name #define ALARM_THRESHOLD_PROPERTY L"AlarmThreshold" //!< 'AlarmThreshold' property #define ALARM_ENABLED_PROPERTY L"AlarmEnabled" //!< 'AlarmEnabled' property #define MEMORY_MODE_PROPERTY L"MemoryMode" //!< 'MemoryMode' property name #define PERSISTENT_MEM_TYPE_PROPERTY L"PersistentMemoryType" //!< 'PersistentMemoryType' property name #define MEMORY_SIZE_PROPERTY L"MemorySize" //!< 'MemorySize' property name #define RESERVED_PROPERTY L"Reserved" //!< 'Reserved' property name #define APPDIRECT_SIZE_PROPERTY L"AppDirectSize" //!< 'AppDirectSize' property name #define APPDIRECT_INDEX_PROPERTY L"AppDirectIndex" //!< 'AppDirectIndex ' property name #define APPDIRECT_1_SIZE_PROPERTY L"AppDirect1Size" //!< 'AppDirect1Size' property name #define APPDIRECT_1_SETTINGS_PROPERTY L"AppDirect1Settings" //!< 'AppDirect1Setting' property name #define APPDIRECT_1_INDEX_PROPERTY L"AppDirect1Index" //!< 'AppDirect1Index' property name #define APPDIRECT_2_SIZE_PROPERTY L"AppDirect2Size" //!< 'AppDirect2Size' property name #define APPDIRECT_2_SETTINGS_PROPERTY L"AppDirect2Settings" //!< 'AppDirect2Setting' property name #define APPDIRECT_2_INDEX_PROPERTY L"AppDirect2Index" //!< 'AppDirect2Index' property name #define MEM_INFO_PAGE_PROPERTY L"Page" //!< 'MemoryInfo page' property name #define LOG_PROPERTY L"Log" //!< 'Log' property name #define PROPERTY_VALUE_0 L"0" //!< Property 0 value #define PROPERTY_VALUE_1 L"1" //!< Property 1 value #define PROPERTY_VALUE_IGNORE L"Ignore" //!< Property 'Ignore' value #define PROPERTY_VALUE_NO L"No" //!< Property 'No' value #define PROPERTY_VALUE_YES L"Yes" //!< Property 'Yes' value #define PROPERTY_VALUE_ENABLED L"Enabled" //!< Property enabled value #define PROPERTY_VALUE_DISABLED L"Disabled" //!< Property disabled value #define SEQUENCE_NUM_PROPERTY L"SequenceNumber" //!< 'error' property name #define COUNT_PROPERTY L"Count" //!< 'error' property name #define LEVEL_PROPERTY L"Level" //!< 'error' property name #define LEVEL_HIGH_PROPERTY_VALUE L"High" //!< 'error' property 'Level' value #define LEVEL_LOW_PROPERTY_VALUE L"Low" //!< 'error' property 'Level' value #define NAMESPACE_ID_PROPERTY L"NamespaceId" #define NAMESPACE_GUID_PROPERTY L"NamespaceGuid" #define CAPACITY_PROPERTY L"Capacity" #define NAME_PROPERTY L"Name" #define HEALTH_PROPERTY L"HealthState" #define REGION_ID_PROPERTY L"RegionID" #define BLOCK_SIZE_PROPERTY L"BlockSize" #define BLOCK_COUNT_PROPERTY L"BlockCount" #define MODE_PROPERTY L"Mode" #define PROPERTY_VALUE_NONE L"None" #define PROPERTY_VALUE_SECTOR L"Sector" #define AVG_PWR_REPORTING_TIME_CONSTANT L"AveragePowerReportingTimeConstant" #define ACCESS_TYPE_PROPERTY L"AccessType" #define ERASE_CAPABLE_PROPERTY L"EraseCapable" #define ENCRYPTION_PROPERTY L"Encryption" #define CLI_DEFAULT_DIMM_ID_PROPERTY L"CLI_DEFAULT_DIMM_ID" #define CLI_DEFAULT_SIZE_PROPERTY L"CLI_DEFAULT_SIZE" #define APP_DIRECT_SETTINGS_PROPERTY L"APPDIRECT_SETTINGS" #define LABEL_VERSION_PROPERTY L"LabelVersion" #define NS_LABEL_VERSION_PROPERTY L"NamespaceLabelVersion" #define SEVERITY_PROPERTY L"Severity" #define PROPERTY_VALUE_UID L"UID" #define PROPERTY_VALUE_HANDLE L"HANDLE" #define PROPERTY_VALUE_AUTO L"AUTO" #define PROPERTY_VALUE_AUTO10 L"AUTO_10" #define PROPERTY_VALUE_RECOMMENDED L"RECOMMENDED" #define CATEGORY_PROPERTY L"Category" #define DBG_LOG_LEVEL L"DBG_LOG_LEVEL" #define CREATE_SUPP_NAME L"Name" #define PROPERTY_ERROR_UNKNOWN L"Reason for failure unknown" #define PROPERTY_ERROR_DEFAULT_DIMM_NOT_PROVIDED L"Default DimmID Type not provided" #define PROPERTY_ERROR_INCORRECT_DEFAULT_DIMM_TYPE L"Incorrect default DimmID type" #define PROPERTY_ERROR_DISPLAY_DEFAULT_NOT_PROVIDED L"Display default size type not provided" #define PROPERTY_ERROR_DEFAULT_INCORRECT_SIZE_TYPE L"Incorrect default size type" #define PROPERTY_ERROR_INTERLEAVE_TYPE_NOT_PROVIDED L"AppDirect interleave setting type not provided" #define PROPERTY_ERROR_APPDIR_INTERLEAVE_TYPE L"Incorrect AppDirect interleave setting type" #define PROPERTY_ERROR_GRANULARITY_NOT_PROVIDED L"AppDirect Granularity setting type not provided" #define PROPERTY_ERROR_INVALID_GRANULARITY L"Invalid granularity" #define PROPERTY_ERROR_INVALID_OUT_OF_RANGE L"Setting is invalid or out of range" #define PROPERTY_ERROR_SET_FAILED_UNKNOWN L"Set operation failed" /** Performance Metric Messages **/ #define DCPMM_PERFORMANCE_MEDIA_READS L"MediaReads" #define DCPMM_PERFORMANCE_MEDIA_WRITES L"MediaWrites" #define DCPMM_PERFORMANCE_READ_REQUESTS L"ReadRequests" #define DCPMM_PERFORMANCE_WRITE_REQUESTS L"WriteRequests" #define DCPMM_PERFORMANCE_TOTAL_MEDIA_READS L"TotalMediaReads" #define DCPMM_PERFORMANCE_TOTAL_MEDIA_WRITES L"TotalMediaWrites" #define DCPMM_PERFORMANCE_TOTAL_READ_REQUESTS L"TotalReadRequests" #define DCPMM_PERFORMANCE_TOTAL_WRITE_REQUESTS L"TotalWriteRequests" /** Sensor Detail Messages **/ #define DIMM_HEALTH_STR_DETAIL L"Health - The current " PMEM_MODULE_STR L" health as reported in the SMART log" #define MEDIA_TEMPERATURE_STR_DETAIL L"MediaTemperature - The current " PMEM_MODULE_STR L" media temperature in Celsius" #define CONTROLLER_TEMPERATURE_STR_DETAIL L"ControllerTemperature - The current " PMEM_MODULE_STR L" controller temperature in Celsius" #define SPARE_CAPACITY_STR_DETAIL L"PercentageRemaining - Remaining " PMEM_MODULES_STR L" life as a percentage value of factory expected\ life spa" #define LATCHED_DIRTY_SHUTDOWN_COUNT_STR_DETAIL L"LatchedDirtyShutdownCount - The number of shutdowns without notification over the lifetime of\ the " PMEM_MODULE_STR #define UNLATCHED_DIRTY_SHUTDOWN_COUNT_STR_DETAIL L"UnlatchedDirtyShutdownCount - The number of shutdowns without notification over the lifetime of\ the " PMEM_MODULE_STR L"." #define POWER_ON_TIME_STR_DETAIL L"PowerOnTime - The total power-on time over the lifetime of the " PMEM_MODULE_STR #define UPTIME_STR_DETAIL L"UpTime - The total power-on time since the last power cycle of the " PMEM_MODULE_STR #define POWER_CYCLES_STR_DETAIL L"PowerCycles - The number of power cycles over the lifetime of the " PMEM_MODULE_STR #define FW_ERROR_COUNT_STR_DETAIL L"FwErrorCount - The total number of firmware error log entries" /** common help messages **/ #define HELP_OPTIONS_DETAILS_TEXT L"Changes the output format." #define HELP_VERBOSE_DETAILS_TEXT L"Change the Debug Level Message Display" #define HELP_ALL_DETAILS_TEXT L"Shows all attributes." #define HELP_DISPLAY_DETAILS_TEXT L"Shows attributes specified in a comma-separated list" #define HELP_FORCE_DETAILS_TEXT L"Suppresses the confirmation from the User to use this operation" #define HELP_UNIT_DETAILS_TEXT L"Desired Unit for display" #define HELP_DDRT_DETAILS_TEXT L"Used to specify DDRT as the desired transport protocol" #define HELP_SMBUS_DETAILS_TEXT L"Used to specify SMBUS as the desired transport protocol" #define HELP_LPAYLOAD_DETAILS_TEXT L"Used to specify large transport payload size" #define HELP_SPAYLOAD_DETAILS_TEXT L"Used to specify small transport payload size" #define HELP_TEXT_DIMM_IDS L"DimmIDs" #define HELP_TEXT_DIMM_ID L"DimmID" #define HELP_TEXT_ATTRIBUTES L"Attributes" #define HELP_TEXT_NAMESPACE_IDS L"NamespaceIDs" #define HELP_TEXT_REGION_ID L"RegionID" #define HELP_TEXT_REGION_IDS L"RegionIDs" #define HELP_TEXT_SOCKET_IDS L"SocketIDs" #define HELP_TEXT_SENSORS L"List of Sensors" #define HELP_TEXT_VALUE L"value" #define HELP_TEXT_COUNT L"count" #define HELP_TEXT_GiB L"GiB" #define HELP_TEXT_GB L"GB" #define HELP_TEXT_STRING L"string" #define HELP_TEXT_ERROR_LOG L"Thermal|Media" #define HELP_TEXT_PERCENT L"0|%%" #define HELP_TEXT_APPDIRECT_SETTINGS PROPERTY_VALUE_RECOMMENDED L"|" L"(IMCSize)_(ChannelSize)" #define HELP_TEXT_NS_LABEL_VERSION L"1.1|1.2" #define HELP_NFIT_DETAILS_TEXT L"Used to specify NFIT as the source" #define HELP_TEXT_DEFAULT_SIZE PROPERTY_VALUE_AUTO L"|" \ PROPERTY_VALUE_AUTO10 L"|" \ UNITS_OPTION_B L"|" \ UNITS_OPTION_MB L"|" \ UNITS_OPTION_MIB L"|" \ UNITS_OPTION_GB L"|" \ UNITS_OPTION_GIB L"|" \ UNITS_OPTION_TB L"|" \ UNITS_OPTION_TIB #define HELP_TEXT_PERSISTENT_MEM_TYPE L"AppDirect|AppDirectNotInterleaved" #define HELP_DBG_LOG_LEVEL L"log level" #define HELP_TEXT_PERFORMANCE_CAT L"Performance Metrics" #define HELP_TEXT_AVG_PWR_REPORTING_TIME_CONSTANT_PROPERTY L"<100, 12000>" #define HELP_TEXT_PERFORMANCE_CAT_DETAILS L"\n " DCPMM_PERFORMANCE_MEDIA_READS \ L"\n " DCPMM_PERFORMANCE_MEDIA_WRITES \ L"\n " DCPMM_PERFORMANCE_READ_REQUESTS \ L"\n " DCPMM_PERFORMANCE_WRITE_REQUESTS\ L"\n " DCPMM_PERFORMANCE_TOTAL_MEDIA_READS \ L"\n " DCPMM_PERFORMANCE_TOTAL_MEDIA_WRITES\ L"\n " DCPMM_PERFORMANCE_TOTAL_READ_REQUESTS\ L"\n " DCPMM_PERFORMANCE_TOTAL_WRITE_REQUESTS #define HELP_TEXT_SENSORS_SHORT L"\n " MEDIA_TEMPERATURE_STR_DETAIL \ L"\n " CONTROLLER_TEMPERATURE_STR_DETAIL \ L"\n " SPARE_CAPACITY_STR_DETAIL #define HELP_TEXT_SENSORS_ALL L"\n " DIMM_HEALTH_STR_DETAIL \ L"\n " MEDIA_TEMPERATURE_STR_DETAIL \ L"\n " CONTROLLER_TEMPERATURE_STR_DETAIL \ L"\n " SPARE_CAPACITY_STR_DETAIL \ L"\n " LATCHED_DIRTY_SHUTDOWN_COUNT_STR_DETAIL \ L"\n " UNLATCHED_DIRTY_SHUTDOWN_COUNT_STR_DETAIL \ L"\n " POWER_ON_TIME_STR_DETAIL \ L"\n " UPTIME_STR_DETAIL \ L"\n " POWER_CYCLES_STR_DETAIL \ L"\n " FW_ERROR_COUNT_STR_DETAIL enum ValueRequirementType { ValueEmpty = 1, ValueOptional = 2, ValueRequired = 3 }; /** Defines a single option of a CLI command **/ struct option { CHAR16 OptionNameShort[OPTION_LEN]; CHAR16 OptionName[OPTION_LEN]; CHAR16 *pOptionValueStr; CONST CHAR16 *pHelp; CONST CHAR16 *pHelpDetails; BOOLEAN Required; UINT8 ValueRequirement; }; /** Defines a single target of a CLI command **/ struct target { CHAR16 TargetName[TARGET_LEN]; CHAR16 *pTargetValueStr; CONST CHAR16 *pHelp; BOOLEAN Required; UINT8 ValueRequirement; }; /** Defines a single property of a CLI command **/ struct property { CHAR16 PropertyName[PROPERTY_KEY_LEN]; CHAR16 PropertyValue[PROPERTY_VALUE_LEN]; CONST CHAR16 *pHelp; BOOLEAN Required; UINT8 ValueRequirement; }; enum DisplayType { ResultsView = 0, ListView = 1, ListView2L = 2, TableView = 3, TableTabView = 4, ErrorView = 5, HelpView = 6, DiagView = 7 }; /** Defines the parts of a CLI command **/ typedef struct Command { CHAR16 verb[VERB_LEN]; struct option options[MAX_OPTIONS]; struct target targets[MAX_TARGETS]; struct property properties[MAX_PROPERTIES]; CONST CHAR16 *pHelp; EFI_STATUS (*run)(struct Command *pCmd); //!< Execute the command BOOLEAN PrinterCtrlSupported; BOOLEAN ExcludeDriverBinding; BOOLEAN Hidden; //!< Never print BOOLEAN ShowHelp; BOOLEAN SyntaxErrorHelpNeeded; BOOLEAN VerbMatch; UINT8 CommandId; UINT8 DispType; CHAR16 DispName[DISP_NAME_LEN]; PRINT_CONTEXT *pPrintCtx; } COMMAND; typedef struct TargetsCombination { CHAR16 *pTargets[MAX_TARGETS]; CHAR16 *pErrString; } TARGETS_COMBINATION; typedef struct CommandInput { UINT32 TokenCount; CHAR16 **ppTokens; } COMMAND_INPUT; typedef struct _DispInfo { CHAR16 Name[DISP_NAME_LEN]; UINT8 Type; CHAR16 Delims[DISP_DELIMS_LEN]; }DispInfo; extern DispInfo gDisplayInfo; /** Add the specified command to the list of supported commands **/ EFI_STATUS RegisterCommand(struct Command *pCommand); /** Validate input targets combination Function compares input targets with invalid sequences @param[in] pInputTargets is a pointer to the list of input command targets @param[out] ppErrorString is a pointer to a pointer to the return error message @retval TRUE if input command targets does not match any known invalid combination @retval FALSE if all targets matches to invalid combination **/ BOOLEAN InputTargetsValid(struct target *pInputTargets, CHAR16 **ppErrorString); /** Free the allocated memory for target values in the CLI command structure. @param[in out] pCommand pointer to the command structure **/ VOID FreeCommandStructure( IN OUT COMMAND *pCommand ); /** The function parse the input string and split it to the tokens The caller function is responsible for deallocation of pCmdInput. The FreeCommandInput function should be used to deallocate memory. @param[in] pCommand The input string @param[out] pCmdInput **/ VOID FillCommandInput( IN CHAR16 *pCommand, OUT struct CommandInput *pCmdInput ); /** Clean up the resources associated with the command list **/ void FreeCommands(); /** Clean up the resources associated with the input **/ void FreeCommandInput(struct CommandInput *pCommandInput); /** Parse the given the command line arguments to identify the correct command. It is the responsibility of the caller function to free the allocated memory for target values in the Command structure. @param[in] the command input @param[in,out] p_command @return The results of the command execution or a syntax error **/ EFI_STATUS Parse(struct CommandInput *pInput, struct Command *pCommand); /** If parsing fails, retrieve a more useful syntax error **/ CHAR16 *getSyntaxError(); /** If parsing fails, set syntax error, but first free old one **/ VOID SetSyntaxError( IN CHAR16 *pSyntaxError ); /** Get the help for a command read from the user. @param[in] pCommand a pointer to the parsed struct Command. @param[in] SingleCommand a BOOLEAN flag indicating if we are trying to match to a single command help or to all commands with the same verb. @retval NULL if the command verb could not be matched to any of the registered commands. Or the pointer to the help message. NOTE: If the return pointer is not NULL, the caller is responsible to free the memory using FreePool. **/ CHAR16 *getCommandHelp( IN struct Command *pCommand, BOOLEAN SingleCommand ); CHAR16 *getOverallCommandHelp(); /** Checks if the Unicode string contains the given character. @param[in] Character is the 16-bit character that we are searching for. @param[in] pInputString is the Unicode (16-bit) string that we want to check. @retval TRUE if the input string contains the character we are searching for. @retval FALSE if the character is not present in the string. **/ BOOLEAN ContainsCharacter( IN CHAR16 Character, IN CONST CHAR16* pInputString ); /** Get the number of properties @param[in] pCmd is a pointer to the struct Command that contains the user input. @param[out] pPropertyCount represents the number of properties defined on the command line @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. @retval EFI_SUCCESS **/ EFI_STATUS GetPropertyCount( IN CONST struct Command *pCmd, IN UINT16 *pPropertyCount ); /** Check if a specific property is found @param[in] pCmd is a pointer to the struct Command that contains the user input. @param[in] pProperty is a CHAR16 string that represents the property we want to find. @retval EFI_SUCCESS if we've found the property. @retval EFI_NOT_FOUND if no such property exists for the given pCmd. @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS ContainsProperty( IN CONST struct Command *pCmd, IN CONST CHAR16 *pProperty ); /** Get a specific property value @param[in] pCmd is a pointer to the struct Command that contains the user input. @param[in] pProperty is a CHAR16 string that represents the property we want to find. @param[out] ppReturnValue is a pointer to a pointer to the 16-bit character string that will contain the return property value. @retval EFI_SUCCESS if we've found the property and the value is set. @retval EFI_NOT_FOUND if no such property exists for the given pCmd. @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS GetPropertyValue( IN CONST struct Command *pCmd, IN CONST CHAR16 *pProperty, OUT CHAR16 **ppReturnValue ); /** Check if a specific option is found **/ BOOLEAN containsOption(CONST struct Command *pCmd, CONST CHAR16 *optionName); /** Check if a specific target is found in the command @param[in] pCmd @param[in] pTarget @retval TRUE if the target has been found @retval FALSE if the target has not been found **/ BOOLEAN ContainTarget( IN CONST struct Command *pCmd, IN CONST CHAR16 *pTarget ); /** Get the value of a specific option NOTE: Returned value needs to be freed by the caller **/ CHAR16 *getOptionValue(CONST struct Command *pCmd, CONST CHAR16 *optionName); /** Get the value of a specific target @param[in] pCmd @param[in] pTarget @retval the target value if the target has been found @retval NULL otherwise **/ CHAR16* GetTargetValue( IN struct Command *pCmd, IN CONST CHAR16 *pTarget ); /** Determine if the specified value is in the specified comma separated display list. **/ BOOLEAN ContainsValue(CONST CHAR16 *displayList, CONST CHAR16 *value); /** Get the value of the units option @param[in] pCmd The input command structure @param[out] pUnitsToDisplay Units to display based on input units option @retval EFI_INVALID_PARAMETER if input parameter is NULL, else EFI_SUCCESS **/ EFI_STATUS GetUnitsOption( IN CONST struct Command *pCmd, OUT UINT16 *pUnitsToDisplay ); /** Sets a display information needed when outputting alternative formats like XML. @param[in] pName is a CHAR16 string that represents the output message. @param[in] Type represents the type of output being displayed. @param[in] pDelims is a CHAR16 string that represents delimiters to use when parsing text output @retval EFI_SUCCESS if the name was copied correctly. @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS SetDisplayInfo( IN CONST CHAR16 *pName, IN CONST UINT8 Type, IN CONST CHAR16 *pDelims ); /** Get display information needed when outputting alternative formats like XML. @param[out] pName is a CHAR16 string that represents the output message. @param[int] NameSize is the size of pName in bytes @param[out] pType represents the type of output being displayed. @param[out] pDelims represents the delimiters to use when parsing text output. @param[int] DelimsSize is the size of pDelims in bytes @retval EFI_SUCCESS if the name was copied correctly. @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS GetDisplayInfo( OUT CHAR16 *pName, IN CONST UINT32 NameSize, OUT UINT8 *pType, OUT CHAR16 *pDelims, IN CONST UINT32 DelimsSize ); /** Execute UpdateCmdCtx (if defined), run, and RunCleanup (if defined). @param[in] pCommand pointer to the command structure @retval EFI_SUCCESS if the name was copied correctly. @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS ExecuteCmd(COMMAND *pCommand); #endif /** _COMMAND_PARSER_H_**/ ipmctl-03.00.00.0485/DcpmPkg/cli/Common.c000066400000000000000000002621601440615110200173120ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "Common.h" #include "NvmDimmCli.h" #include #include #include #include #include #include #include #include #include #ifdef OS_BUILD #include #include #endif CONST CHAR16 *mpImcSize[] = { L"Unknown", L"64B", L"128B", L"256B", L"4KB", L"1GB" }; CONST CHAR16 *mpChannelSize[] = { L"Unknown", L"64B", L"128B", L"256B", L"4KB", L"1GB" }; typedef enum { Unknown, Interleave_64B, Interleave_128B, Interleave_256B, Interleave_4KB, Interleave_1GB } InterleaveSizeIndex; typedef enum { ChannelWays_X1 = 1, ChannelWays_X2 = 2, ChannelWays_X3 = 3, ChannelWays_X4 = 4, ChannelWays_X6 = 6, ChannelWays_X8 = 8, ChannelWays_X12 = 12, ChannelWays_X16 = 16, ChannelWays_X24 = 24 } ChannelWaysNumber; CONST CHAR16 *mpDefaultSizeStrs[DISPLAY_SIZE_MAX_SIZE] = { PROPERTY_VALUE_AUTO, PROPERTY_VALUE_AUTO10, UNITS_OPTION_B, UNITS_OPTION_MB, UNITS_OPTION_MIB, UNITS_OPTION_GB, UNITS_OPTION_GIB, UNITS_OPTION_TB, UNITS_OPTION_TIB }; CONST CHAR16 *mpDefaultDimmIds[DISPLAY_DIMM_ID_MAX_SIZE] = { PROPERTY_VALUE_HANDLE, PROPERTY_VALUE_UID, }; /** Compare DimmID field in DIMM_INFO Struct @param[in] pFirst First item to compare @param[in] pSecond Second item to compare @retval -1 if first is less than second @retval 0 if first is equal to second @retval 1 if first is greater than second **/ STATIC INT32 CompareDimmIdInDimmInfo( IN VOID *pFirst, IN VOID *pSecond ) { DIMM_INFO *pDimmInfo = NULL; DIMM_INFO *pDimmInfo2 = NULL; if (pFirst == NULL || pSecond == NULL) { NVDIMM_DBG("NULL pointer found."); return 0; } pDimmInfo = (DIMM_INFO*)pFirst; pDimmInfo2 = (DIMM_INFO*)pSecond; if (pDimmInfo->DimmID < pDimmInfo2->DimmID) { return -1; } else if (pDimmInfo->DimmID > pDimmInfo2->DimmID) { return 1; } else { return 0; } } /** Retrieve a populated array and count of DIMMs in the system. The caller is responsible for freeing the returned array @param[in] pNvmDimmConfigProtocol A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pCmd A pointer to a COMMAND struct. Used to obtain the Printer context. printed to stdout, otherwise will be directed to the printer module. @param[in] dimmInfoCategories Categories that will be populated in the DIMM_INFO struct. @param[out] ppDimms A pointer to the dimm list found in NFIT. @param[out] pDimmCount A pointer to the number of DIMMs found in NFIT. @retval EFI_SUCCESS the dimm list was returned properly @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_NOT_FOUND dimm not found **/ EFI_STATUS GetDimmList( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN struct Command *pCmd, IN DIMM_INFO_CATEGORIES dimmInfoCategories, OUT DIMM_INFO **ppDimms, OUT UINT32 *pDimmCount ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pNvmDimmConfigProtocol == NULL || ppDimms == NULL || pDimmCount == NULL || pCmd == NULL) { NVDIMM_CRIT("NULL input parameter.\n"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetDimmCount(pNvmDimmConfigProtocol, pDimmCount); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed on GetDimmCount."); goto Finish; } if (*pDimmCount == 0) { ReturnCode = EFI_NOT_FOUND; goto Finish; } *ppDimms = AllocateZeroPool(sizeof(**ppDimms) * (*pDimmCount)); if (*ppDimms == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } /** retrieve the DIMM list **/ ReturnCode = pNvmDimmConfigProtocol->GetDimms(pNvmDimmConfigProtocol, *pDimmCount, dimmInfoCategories, *ppDimms); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed to retrieve the DIMM inventory"); goto FinishError; } ReturnCode = BubbleSort((VOID*)*ppDimms, *pDimmCount, sizeof(**ppDimms), CompareDimmIdInDimmInfo); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Dimms list may not be sorted"); goto FinishError; } goto Finish; FinishError: FREE_POOL_SAFE(*ppDimms); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve a populated array and count of all DCPMMs (functional and non-functional) in the system. The caller is responsible for freeing the returned array @param[in] pNvmDimmConfigProtocol A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pCmd A pointer to a COMMAND struct. Used to obtain the Printer context. printed to stdout, otherwise will be directed to the printer module. @param[in] dimmInfoCategories Categories that will be populated in the DIMM_INFO struct. @param[out] ppDimms A pointer to a combined DCPMM list (initialized and uninitialized) from NFIT. @param[out] pDimmCount A pointer to the total number of DCPMMs found in NFIT. @retval EFI_SUCCESS the dimm list was returned properly @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_NOT_FOUND dimm not found **/ EFI_STATUS GetAllDimmList( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN struct Command *pCmd, IN DIMM_INFO_CATEGORIES dimmInfoCategories, OUT DIMM_INFO **ppDimms, OUT UINT32 *pDimmCount ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Index = 0; UINT32 UninitializedDimmCount = 0; UINT32 InitializedDimmCount = 0; NVDIMM_ENTRY(); if (pNvmDimmConfigProtocol == NULL || ppDimms == NULL || pDimmCount == NULL || pCmd == NULL) { NVDIMM_CRIT("NULL input parameter.\n"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetDimmCount(pNvmDimmConfigProtocol, &InitializedDimmCount); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed on GetDimmCount."); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetUninitializedDimmCount(pNvmDimmConfigProtocol, &UninitializedDimmCount); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } if (0 == (InitializedDimmCount + UninitializedDimmCount)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_DIMMS); goto Finish; } *pDimmCount = InitializedDimmCount + UninitializedDimmCount; *ppDimms = AllocateZeroPool(sizeof(**ppDimms) * (*pDimmCount)); if (*ppDimms == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } /** retrieve the DIMM list **/ ReturnCode = pNvmDimmConfigProtocol->GetDimms(pNvmDimmConfigProtocol, InitializedDimmCount, dimmInfoCategories, *ppDimms); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed to retrieve the DIMM inventory"); goto FinishError; } // Append the uninitialized dimms after the initialized dimms in the dimms array ReturnCode = pNvmDimmConfigProtocol->GetUninitializedDimms(pNvmDimmConfigProtocol, UninitializedDimmCount, &((*ppDimms)[InitializedDimmCount])); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_WARN("Failed to retrieve the uninitialized DIMM inventory"); goto FinishError; } // Fill in the dimmInfoCategories for the uninitialized dimms for (Index = InitializedDimmCount; Index < *pDimmCount; Index++) { ReturnCode = pNvmDimmConfigProtocol->GetDimm(pNvmDimmConfigProtocol, (*ppDimms)[Index].DimmID, dimmInfoCategories, &((*ppDimms)[Index])); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_WARN("Failed to populate the uninitialized DIMM inventory"); goto FinishError; } } ReturnCode = BubbleSort((VOID*)*ppDimms, *pDimmCount, sizeof(**ppDimms), CompareDimmIdInDimmInfo); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Dimms list may not be sorted"); goto FinishError; } goto Finish; FinishError: FREE_POOL_SAFE(*ppDimms); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Parse the string and return the array of unsigned integers Example String: "1,3,7" Array[0]: 1 Array[1]: 3 Array[2]: 7 @param[in] pString string to parse @param[out] ppUints allocated, filled array with the uints @param[out] pUintsNum size of uints array @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER the format of string is not proper **/ EFI_STATUS GetUintsFromString( IN CHAR16 *pString, OUT UINT16 **ppUints, OUT UINT32 *pUintsNum ) { EFI_STATUS Rc = EFI_SUCCESS; UINT32 Index = 0; CHAR16 **ppUintsStr = NULL; UINTN ParsedNumber = 0; BOOLEAN IsNumber = FALSE; NVDIMM_ENTRY(); if (pString == NULL || ppUints == NULL || pUintsNum == NULL) { Rc = EFI_INVALID_PARAMETER; goto Finish; } /** No targets specified - select all targets (If value is required - command won't pass parsing process.) **/ if (StrLen(pString) == 0) { *ppUints = NULL; *pUintsNum = 0; Rc = EFI_SUCCESS; goto Finish; } ppUintsStr = StrSplit(pString, L',', pUintsNum); if (ppUintsStr == NULL) { Rc = EFI_OUT_OF_RESOURCES; goto Finish; } *ppUints = AllocateZeroPool(*pUintsNum * sizeof(**ppUints)); if (*ppUints == NULL) { Rc = EFI_OUT_OF_RESOURCES; goto FinishError; } for (Index = 0; Index < *pUintsNum; Index++) { IsNumber = GetU64FromString(ppUintsStr[Index], &ParsedNumber); if (!IsNumber) { Rc = EFI_INVALID_PARAMETER; goto FinishError; } (*ppUints)[Index] = (UINT16)ParsedNumber; } goto Finish; FinishError: FREE_POOL_SAFE(*ppUints); Finish: FreeStringArray(ppUintsStr, pUintsNum == NULL ? 0 : *pUintsNum); NVDIMM_EXIT_I64(Rc); return Rc; } /** Parses the dimm target string (which can contain DimmIDs as NFIT handles and/or DimmUIDs), and returns an array of DimmIDs in the SMBIOS physical-id forms. Also checks for invalid DimmIDs and duplicate entries. Example String: "8089-00-0000-76543210,30,0x0022" Array[0]: 28 Array[1]: 30 Array[2]: 34 @param[in] pCmd A pointer to a COMMAND struct. Used to obtain the Printer context. @param[in] pDimmString The dimm target string to parse. @param[in] pDimmInfo The dimm list found in NFIT. @param[in] DimmCount Size of the pDimmInfo array. @param[out] ppDimmIds Pointer to the array allocated and filled with the SMBIOS DimmIDs. @param[out] pDimmIdsCount Size of the pDimmIds array. @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER inputs are null, the format of string is not proper, duplicated Dimm IDs @retval EFI_NOT_FOUND dimm not found **/ EFI_STATUS GetDimmIdsFromString( IN struct Command *pCmd, IN CHAR16 *pDimmString, IN DIMM_INFO *pDimmInfo, IN UINT32 DimmCount, OUT UINT16 **ppDimmIds, OUT UINT32 *pDimmIdsCount ) { EFI_STATUS Rc = EFI_SUCCESS; CHAR16 **ppDimmIdTokensStr = NULL; UINT32 *pParsedDimmIdNumber = NULL; UINT64 DimmIdNumberTmp = 0; BOOLEAN *pIsDimmIdNumber = NULL; BOOLEAN DimmIdFound = FALSE; UINT32 Index = 0; UINT32 Index2 = 0; NVDIMM_ENTRY(); if ((pDimmString == NULL) || (pDimmInfo == NULL) || (ppDimmIds == NULL) || (pDimmIdsCount == NULL) || (pCmd == NULL)) { NVDIMM_CRIT("NULL input parameter.\n"); Rc = EFI_INVALID_PARAMETER; goto Finish; } /** No DIMM targets specified - select all targets (If value is required - command won't pass parsing process.) **/ if (StrLen(pDimmString) == 0) { *ppDimmIds = NULL; *pDimmIdsCount = 0; Rc = EFI_SUCCESS; goto Finish; } ppDimmIdTokensStr = StrSplit(pDimmString, L',', pDimmIdsCount); if (ppDimmIdTokensStr == NULL) { Rc = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pCmd->pPrintCtx, Rc, CLI_ERR_OUT_OF_MEMORY); goto Finish; } *ppDimmIds = AllocateZeroPool(*pDimmIdsCount * sizeof(**ppDimmIds)); pParsedDimmIdNumber = AllocateZeroPool(*pDimmIdsCount * sizeof(*pParsedDimmIdNumber)); pIsDimmIdNumber = AllocateZeroPool(*pDimmIdsCount * sizeof(*pIsDimmIdNumber)); if ((*ppDimmIds == NULL) || (pParsedDimmIdNumber == NULL) || (pIsDimmIdNumber == NULL)) { Rc = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pCmd->pPrintCtx, Rc, CLI_ERR_OUT_OF_MEMORY); goto FinishError; } for (Index = 0; Index < *pDimmIdsCount; Index++) { pIsDimmIdNumber[Index] = GetU64FromString(ppDimmIdTokensStr[Index], &DimmIdNumberTmp); if ((pIsDimmIdNumber[Index]) && (DimmIdNumberTmp > MAX_UINT32)) { Rc = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pCmd->pPrintCtx, Rc, L"DimmID size cannot exceed 32 bits. Invalid DimmID: " FORMAT_STR_NL, ppDimmIdTokensStr[Index]); goto FinishError; } pParsedDimmIdNumber[Index] = (UINT32)DimmIdNumberTmp; DimmIdNumberTmp = 0; } for (Index = 0; Index < *pDimmIdsCount; Index++) { DimmIdFound = FALSE; /** Checking if the specified DIMMs exist **/ for (Index2 = 0; Index2 < DimmCount; Index2++) { if ((!pIsDimmIdNumber[Index] && StrICmp(ppDimmIdTokensStr[Index], pDimmInfo[Index2].DimmUid) == 0) || (pIsDimmIdNumber[Index] && pDimmInfo[Index2].DimmHandle == pParsedDimmIdNumber[Index])) { // This DimmID is unique for all dimms on the platform regardless of // state and is assigned by UEFI FW. We use it for all our APIs. // Handle seems to be a better identifier since it corresponds to the // position on the board, but this is good enough and cheap to look up. (*ppDimmIds)[Index] = pDimmInfo[Index2].DimmID; DimmIdFound = TRUE; break; } } if (!DimmIdFound) { Rc = EFI_NOT_FOUND; PRINTER_SET_MSG(pCmd->pPrintCtx, Rc, PMEM_MODULE_STR L" not found. Invalid DimmID: " FORMAT_STR_NL, ppDimmIdTokensStr[Index]); goto FinishError; } } /** Checking for duplicate entries **/ for (Index = 0; Index < *pDimmIdsCount; Index++) { for (Index2 = (Index + 1); Index2 < *pDimmIdsCount; Index2++) { if ((*ppDimmIds)[Index] == (*ppDimmIds)[Index2]) { Rc = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pCmd->pPrintCtx, Rc, L"Duplicated DimmID: " FORMAT_STR_NL, ppDimmIdTokensStr[Index2]); goto FinishError; } } } goto Finish; FinishError: FREE_POOL_SAFE(*ppDimmIds); Finish: FreeStringArray(ppDimmIdTokensStr, pDimmIdsCount == NULL ? 0 : *pDimmIdsCount); FREE_POOL_SAFE(pParsedDimmIdNumber); FREE_POOL_SAFE(pIsDimmIdNumber); NVDIMM_EXIT_I64(Rc); return Rc; } /** Parses the dimm target string (which can contain DimmIDs as SMBIOS type-17 handles and/or DimmUIDs), and returns a DimmUid. Example String: "8089-00-0000-13325476" or "30" or "0x0022" @param[in] pDimmString The dimm target string to parse. @param[in] pDimmInfo The dimm list found in NFIT. @param[in] DimmCount Size of the pDimmInfo array. @param[out] pDimmUid Pointer to the NVM_UID buffer. @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER the format of string is not proper @retval EFI_NOT_FOUND dimm not found **/ EFI_STATUS GetDimmUidFromString( IN CHAR16 *pDimmString, IN DIMM_INFO *pDimmInfo, IN UINT32 DimmCount, OUT CHAR8 *pDimmUid ) { EFI_STATUS Rc = EFI_SUCCESS; UINT32 ParsedDimmIdNumber; UINT64 DimmIdNumberTmp = 0; BOOLEAN IsDimmIdNumber; BOOLEAN DimmIdFound = FALSE; UINT32 Index = 0; NVDIMM_ENTRY(); if ((pDimmString == NULL) || (pDimmInfo == NULL) || (pDimmUid == NULL)) { NVDIMM_CRIT("NULL input parameter.\n"); Rc = EFI_INVALID_PARAMETER; goto Finish; } /** No DIMM targets specified - select all targets (If value is required - command won't pass parsing process.) **/ if (StrLen(pDimmString) == 0) { Rc = EFI_SUCCESS; goto Finish; } IsDimmIdNumber = GetU64FromString(pDimmString, &DimmIdNumberTmp); if ((IsDimmIdNumber) && (DimmIdNumberTmp > MAX_UINT32)) { NVDIMM_DBG("DimmID size cannot exceed 32 bits."); Rc = EFI_INVALID_PARAMETER; goto Finish; } ParsedDimmIdNumber = (UINT32)DimmIdNumberTmp; DimmIdNumberTmp = 0; DimmIdFound = FALSE; /** Checking if the specified DIMMs exist **/ for (Index = 0; Index < DimmCount; Index++) { if ((!IsDimmIdNumber && StrICmp(pDimmString, pDimmInfo[Index].DimmUid) == 0) || (IsDimmIdNumber && pDimmInfo[Index].DimmHandle == ParsedDimmIdNumber)) { UnicodeStrToAsciiStrS(pDimmInfo[Index].DimmUid, pDimmUid, MAX_DIMM_UID_LENGTH); DimmIdFound = TRUE; break; } } if (!DimmIdFound) { Rc = EFI_NOT_FOUND; NVDIMM_DBG("DIMM not found."); } Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** Check if the uint is in the uints array @param[in] pUints array of the uints @param[in] UintsNum number of uints in the array @param[in] UintToFind searched uint @retval TRUE if the uint has been found @retval FALSE if the uint has not been found **/ BOOLEAN ContainUint( IN UINT16 *pUints, IN UINT32 UintsNum, IN UINT16 UintToFind ) { UINT32 Index; BOOLEAN ReturnCode = FALSE; NVDIMM_ENTRY(); if (pUints == NULL) { ReturnCode = FALSE; goto Finish; } for (Index = 0; Index < UintsNum; Index++) { if (pUints[Index] == UintToFind) { ReturnCode = TRUE; goto Finish; } } Finish: NVDIMM_EXIT_I64(ReturnCode ? EFI_SUCCESS : EFI_ABORTED); return ReturnCode; } /** Check if the Guid is in the Guids array @param[in] ppGuids array of the Guid pointers @param[in] GuidsNum number of Guids in the array @param[in] pGuidToFind pointer to GUID with information to find @retval TRUE if table contains guid with same data as *pGuidToFind @retval FALSE **/ BOOLEAN ContainGuid( IN GUID **ppGuids, IN UINT32 GuidsNum, IN GUID *pGuidToFind ) { UINT32 Index; BOOLEAN ReturnCode = FALSE; NVDIMM_ENTRY(); if (ppGuids == NULL || pGuidToFind == NULL) { ReturnCode = FALSE; goto Finish; } for (Index = 0; Index < GuidsNum; Index++) { if (CompareMem(ppGuids[Index], pGuidToFind, sizeof(GUID)) == 0) { ReturnCode = TRUE; goto Finish; } } Finish: NVDIMM_EXIT_I64(ReturnCode ? EFI_SUCCESS : EFI_ABORTED); return ReturnCode; } /** Gets number of Manageable and supported Dimms and their IDs @param[in] pNvmDimmConfigProtocol A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] CheckSupportedConfigDimm If true, include dimms in unmapped set of dimms (non-POR) in returned dimm list. If false, skip these dimms from returned list. @param[out] DimmIdsCount is the pointer to variable, where number of dimms will be stored. @param[out] ppDimmIds is the pointer to variable, where IDs of dimms will be stored. @retval EFI_NOT_FOUND if the connection with NvmDimmProtocol can't be established @retval EFI_OUT_OF_RESOURCES if the memory allocation fails. @retval EFI_INVALID_PARAMETER if number of dimms or dimm IDs have not been assigned properly. @retval EFI_SUCCESS if successfully assigned number of dimms and IDs to variables. **/ EFI_STATUS GetManageableDimmsNumberAndId( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN BOOLEAN CheckSupportedConfigDimm, OUT UINT32 *pDimmIdsCount, OUT UINT16 **ppDimmIds ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM_INFO *pDimms = NULL; UINT16 Index = 0; UINT16 NewListIndex = 0; NVDIMM_ENTRY(); if (pDimmIdsCount == NULL || ppDimmIds == NULL || pNvmDimmConfigProtocol == NULL) { NVDIMM_CRIT("NULL input parameter.\n"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetDimmCount(pNvmDimmConfigProtocol, pDimmIdsCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Error: Communication with the device driver failed."); goto Finish; } pDimms = AllocateZeroPool(sizeof(*pDimms) * (*pDimmIdsCount)); *ppDimmIds = AllocateZeroPool(sizeof(**ppDimmIds) * (*pDimmIdsCount)); if (pDimms == NULL || *ppDimmIds == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Error: Out of memory\n"); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetDimms(pNvmDimmConfigProtocol, *pDimmIdsCount, DIMM_INFO_CATEGORY_NONE, pDimms); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed to retrieve the DIMM inventory found in NFIT"); goto Finish; } for (Index = 0; Index < *pDimmIdsCount; Index++) { if ((!CheckSupportedConfigDimm && (pDimms[Index].ManageabilityState == MANAGEMENT_VALID_CONFIG)) ||((CheckSupportedConfigDimm && !pDimms[Index].IsInPopulationViolation) && pDimms[Index].ManageabilityState == MANAGEMENT_VALID_CONFIG)){ (*ppDimmIds)[NewListIndex] = pDimms[Index].DimmID; NewListIndex++; } } *pDimmIdsCount = NewListIndex; if (NewListIndex == 0) { ReturnCode = NVM_ERR_MANAGEABLE_DIMM_NOT_FOUND; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Gets number of Manageable (functional and non-functional) and supported Dimms and their IDs @param[in] pNvmDimmConfigProtocol A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] CheckSupportedConfigDimm If true, include dimms in unmapped set of dimms (non-POR) in returned dimm list. If false, skip these dimms from returned list. @param[out] DimmIdsCount is the pointer to variable, where number of dimms will be stored. @param[out] ppDimmIds is the pointer to variable, where IDs of dimms will be stored. @retval EFI_NOT_FOUND if the connection with NvmDimmProtocol can't be established @retval EFI_OUT_OF_RESOURCES if the memory allocation fails. @retval EFI_INVALID_PARAMETER if number of dimms or dimm IDs have not been assigned properly. @retval EFI_SUCCESS if successfully assigned number of dimms and IDs to variables. **/ EFI_STATUS GetAllManageableDimmsNumberAndId( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN BOOLEAN CheckSupportedConfigDimm, OUT UINT32 *pDimmIdsCount, OUT UINT16 **ppDimmIds ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM_INFO *pDimms = NULL; UINT32 Index = 0; UINT32 NewListIndex = 0; UINT32 UninitializedDimmCount = 0; UINT32 InitializedDimmCount = 0; NVDIMM_ENTRY(); if (pDimmIdsCount == NULL || ppDimmIds == NULL || pNvmDimmConfigProtocol == NULL) { NVDIMM_CRIT("NULL input parameter.\n"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetDimmCount(pNvmDimmConfigProtocol, &InitializedDimmCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Error: Communication with the device driver failed."); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetUninitializedDimmCount(pNvmDimmConfigProtocol, &UninitializedDimmCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Error: Communication with the device driver failed."); goto Finish; } if (0 == (InitializedDimmCount + UninitializedDimmCount)) { ReturnCode = EFI_NOT_FOUND; goto Finish; } *pDimmIdsCount = InitializedDimmCount + UninitializedDimmCount; pDimms = AllocateZeroPool(sizeof(*pDimms) * (*pDimmIdsCount)); *ppDimmIds = AllocateZeroPool(sizeof(**ppDimmIds) * (*pDimmIdsCount)); if (pDimms == NULL || *ppDimmIds == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Error: Out of memory\n"); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetDimms(pNvmDimmConfigProtocol, *pDimmIdsCount, DIMM_INFO_CATEGORY_NONE, pDimms); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed to retrieve the DIMM inventory found in NFIT"); goto Finish; } // Append the uninitialized dimms after the initialized dimms in the dimms array ReturnCode = pNvmDimmConfigProtocol->GetUninitializedDimms(pNvmDimmConfigProtocol, UninitializedDimmCount, &((pDimms)[InitializedDimmCount])); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to retrieve the uninitialized DIMM inventory"); goto Finish; } // Fill in the dimmInfoCategories for the uninitialized dimms for (Index = InitializedDimmCount; Index < *pDimmIdsCount; Index++) { ReturnCode = pNvmDimmConfigProtocol->GetDimm(pNvmDimmConfigProtocol, (pDimms)[Index].DimmID, DIMM_INFO_CATEGORY_NONE, &((pDimms)[Index])); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to populate the uninitialized DIMM inventory"); goto Finish; } } ReturnCode = BubbleSort((VOID*)pDimms, *pDimmIdsCount, sizeof(*pDimms), CompareDimmIdInDimmInfo); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Dimms list may not be sorted"); goto Finish; } for (Index = 0; Index < *pDimmIdsCount; Index++) { if ((!CheckSupportedConfigDimm && (pDimms[Index].ManageabilityState == MANAGEMENT_VALID_CONFIG)) || ((CheckSupportedConfigDimm && !pDimms[Index].IsInPopulationViolation) && pDimms[Index].ManageabilityState == MANAGEMENT_VALID_CONFIG)) { (*ppDimmIds)[NewListIndex] = pDimms[Index].DimmID; NewListIndex++; } } *pDimmIdsCount = NewListIndex; if (NewListIndex == 0) { ReturnCode = NVM_ERR_MANAGEABLE_DIMM_NOT_FOUND; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Checks if the provided display list string contains only the valid values. @param[in] pDisplayValues pointer to the Unicode string containing the user input display list. @param[in] ppAllowedDisplayValues pointer to an array of Unicode strings that define the valid display values. @param[in] Count is the number of valid display values in ppAllowedDisplayValues. @retval EFI_SUCCESS if all of the provided display values are valid. @retval EFI_OUT_OF_RESOURCES if the memory allocation fails. @retval EFI_INVALID_PARAMETER if one or more of the provided display values is not a valid one. Or if pDisplayValues or ppAllowedDisplayValues is NULL. **/ EFI_STATUS CheckDisplayList( IN CHAR16 *pDisplayValues, IN CHAR16 **ppAllowedDisplayValues, IN UINT16 Count ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 **ppSplitDisplayValues = NULL; UINT32 SplitDisplayValuesSize = 0; UINT32 Index = 0; UINT32 Index2 = 0; BOOLEAN CorrectDisplayValue = FALSE; NVDIMM_ENTRY(); if (pDisplayValues == NULL || ppAllowedDisplayValues == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ppSplitDisplayValues = StrSplit(pDisplayValues, L',', &SplitDisplayValuesSize); if (ppSplitDisplayValues == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } for (Index = 0; Index < SplitDisplayValuesSize; Index++) { CorrectDisplayValue = FALSE; for (Index2 = 0; Index2 < Count; Index2++) { // Check through all of the valid values if (StrICmp(ppSplitDisplayValues[Index], ppAllowedDisplayValues[Index2]) == 0) { CorrectDisplayValue = TRUE; // This value is allowed break; // If we find a match, leave the loop } } if (!CorrectDisplayValue) { // If this value is not allowed, set the return code. ReturnCode = EFI_INVALID_PARAMETER; } } FreeStringArray(ppSplitDisplayValues, SplitDisplayValuesSize); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Checks if user has specified the options -a|-all and -d|-display. Those two flags exclude each other so the function also checks if the user didn't provide them both. If the -d|-display option has been found, the its values are checked against the allowed values for this parameter. @param[in] pCommand is the pointer to a Command structure that is tested for the options presence. @param[in] ppAllowedDisplayValues is a pointer to an array of Unicode strings considered as the valid values for the -d|-display option. @param[in] AllowedDisplayValuesCount is a UINT32 value that represents the number of elements in the array pointed by ppAllowedDisplayValues. @param[out] pDispOptions contains the following. A BOOLEAN value that will represent the presence of the -a|-all option in the Command pointed by pCommand. A BOOLEAN value that will represent the presence of the -d|-display option in the Command pointed by pCommand. A pointer to an Unicode string. If the -d|-display option is present, this pointer will be set to the option value Unicode string. @retval EFI_SUCCESS the check went fine, there were no errors @retval EFI_INVALID_PARAMETER if the user provided both options, the display option has been provided and has some invalid values or if at least one of the input pointer parameters is NULL. @retval EFI_OUT_OF_RESOURCES if the memory allocation fails. **/ EFI_STATUS CheckAllAndDisplayOptions( IN struct Command *pCommand, IN CHAR16 **ppAllowedDisplayValues, IN UINT32 AllowedDisplayValuesCount, OUT CMD_DISPLAY_OPTIONS *pDispOptions ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 *pDisplayValues = NULL; NVDIMM_ENTRY(); if (pDispOptions == NULL || ppAllowedDisplayValues == NULL || pCommand == NULL) { NVDIMM_CRIT("NULL input parameter.\n"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /** if the all option was specified **/ if (containsOption(pCommand, ALL_OPTION) || containsOption(pCommand, ALL_OPTION_SHORT)) { pDispOptions->AllOptionSet = TRUE; } /** if the display option was specified **/ pDisplayValues = getOptionValue(pCommand, DISPLAY_OPTION); if (pDisplayValues) { pDispOptions->DisplayOptionSet = TRUE; } else { pDisplayValues = getOptionValue(pCommand, DISPLAY_OPTION_SHORT); if (pDisplayValues) { pDispOptions->DisplayOptionSet = TRUE; } } pDispOptions->pDisplayValues = pDisplayValues; /** make sure they didn't specify both the all and display options **/ if (pDispOptions->AllOptionSet && pDispOptions->DisplayOptionSet) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pCommand->pPrintCtx, ReturnCode, CLI_ERR_OPTIONS_ALL_DISPLAY_USED_TOGETHER); goto Finish; } /** Check that the display parameters are correct (if display option is set) **/ if (pDispOptions->DisplayOptionSet) { ReturnCode = CheckDisplayList(pDisplayValues, ppAllowedDisplayValues, (UINT16)AllowedDisplayValuesCount); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCommand->pPrintCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_OPTION_DISPLAY); } } /** Set the text output type (table vs. list view) **/ if(pCommand->pPrintCtx && pCommand->PrinterCtrlSupported == TRUE) { if (!(pDispOptions->AllOptionSet) && !(pDispOptions->DisplayOptionSet)) { PRINTER_ENABLE_TEXT_TABLE_FORMAT(pCommand->pPrintCtx); } else { PRINTER_ENABLE_LIST_TABLE_FORMAT(pCommand->pPrintCtx); } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve property by name and assign its value to UINT64. @param[in] pCmd Command containing the property @param[in] pPropertyName String with property name @param[out] pOutValue target UINT64 value @retval FALSE if there was no such property or it doesn't contain a valid value **/ BOOLEAN PropertyToUint64( IN struct Command *pCmd, IN CHAR16 *pPropertyName, OUT UINT64 *pOutValue ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; BOOLEAN IsValid = FALSE; CHAR16 *pStringValue = NULL; ReturnCode = GetPropertyValue(pCmd, pPropertyName, &pStringValue); if (EFI_ERROR(ReturnCode)) { goto Finish; } IsValid = GetU64FromString(pStringValue, pOutValue); Finish: return IsValid; } /** Retrieve property by name and assign its value to double @param[in] pCmd Command containing the property @param[in] pPropertyName String with property name @param[out] pOutValue Target double value @retval EFI_INVALID_PARAMETER Property not found or no valid value inside @retval EFI_SUCCESS Conversion successful **/ EFI_STATUS PropertyToDouble( IN struct Command *pCmd, IN CHAR16 *pPropertyName, OUT double *pOutValue ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CHAR16 *pPropertyValue = NULL; if (pCmd == NULL || pPropertyName == NULL || pOutValue == NULL) { goto Finish; } ReturnCode = GetPropertyValue(pCmd, pPropertyName, &pPropertyValue); if (EFI_ERROR(ReturnCode) || pPropertyValue == NULL) { goto Finish; } ReturnCode = StringToDouble(gNvmDimmCliHiiHandle, pPropertyValue, pOutValue); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = EFI_SUCCESS; NVDIMM_DBG("Converted %s string to %f double", pPropertyValue, *pOutValue); Finish: return ReturnCode; } /** Extracts working directory path from file path @param[in] pUserFilePath Pointer to string with user specified file path @param[out] pOutFilePath Pointer to actual file path @param[out] ppDevicePath Pointer to where to store device path @retval EFI_SUCCESS Extraction success @retval EFI_INVALID_PARAMETER Invalid parameter @retval EFI_OUT_OF_RESOURCES Out of resources **/ EFI_STATUS GetDeviceAndFilePath( IN CHAR16 *pUserFilePath, OUT CHAR16 *pOutFilePath, OUT EFI_DEVICE_PATH_PROTOCOL **ppDevicePath ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_SHELL_PROTOCOL *pEfiShell = NULL; EFI_DEVICE_PATH_PROTOCOL *pDevPathInternal = NULL; EFI_HANDLE *pHandles = NULL; UINTN HandlesCount = 0; CHAR16 *pTmpFilePath = NULL; CHAR16 *pTmpWorkingDir = NULL; CONST CHAR16* pCurDir = NULL; CHAR16 *pCurDirPath = NULL; NVDIMM_ENTRY(); if (pUserFilePath == NULL || pOutFilePath == NULL || ppDevicePath == NULL) { goto Finish; } #ifdef OS_BUILD StrnCpyS(pOutFilePath, OPTION_VALUE_LEN, pUserFilePath, OPTION_VALUE_LEN - 1); return EFI_SUCCESS; #endif pTmpWorkingDir = AllocateZeroPool(OPTION_VALUE_LEN * sizeof(*pTmpWorkingDir)); if (pTmpWorkingDir == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } // Add " .\ "(current dir) to the file path if relative path is specified if (!ContainsCharacter(L':', pUserFilePath)) { pCurDirPath = CatSPrint(NULL, L".\\" FORMAT_STR, pUserFilePath); } else { pCurDirPath = CatSPrint(NULL, FORMAT_STR, pUserFilePath); } if (pCurDirPath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } // Get Efi Shell Protocol ReturnCode = gBS->LocateHandleBuffer(ByProtocol, &gEfiShellProtocolGuid, NULL, &HandlesCount, &pHandles); if (EFI_ERROR(ReturnCode) || HandlesCount >= MAX_SHELL_PROTOCOL_HANDLES) { NVDIMM_WARN("Error while opening the shell protocol. Code: " FORMAT_EFI_STATUS "", ReturnCode); ReturnCode = EFI_NOT_FOUND; goto Finish; } ReturnCode = gBS->OpenProtocol( pHandles[0], &gEfiShellProtocolGuid, (VOID *)&pEfiShell, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error while opening the shell protocol. Code: " FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } // If User has not typed "Fsx:\", get current working directory if (!ContainsCharacter(L':', pCurDirPath)) { // Otherwise, path is relative to current directory pCurDir = pEfiShell->GetCurDir(NULL); if (pCurDir == NULL) { NVDIMM_DBG("Error while getting the Working Directory."); goto Finish; } if (StrLen(pCurDir) + 1 > OPTION_VALUE_LEN) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } StrnCpyS(pTmpWorkingDir, OPTION_VALUE_LEN, pCurDir, OPTION_VALUE_LEN - 1); // Take null terminator into account StrnCatS(pTmpWorkingDir, OPTION_VALUE_LEN, pCurDirPath, OPTION_VALUE_LEN - StrLen(pTmpWorkingDir) - 1); } else { StrnCpyS(pTmpWorkingDir, OPTION_VALUE_LEN, pCurDirPath, OPTION_VALUE_LEN - 1); } // Extract working directory pTmpFilePath = pTmpWorkingDir; while (pTmpFilePath[0] != L'\\' && pTmpFilePath[0] != L'\0') { pTmpFilePath++; } StrnCpyS(pOutFilePath, OPTION_VALUE_LEN, pTmpFilePath, OPTION_VALUE_LEN - 1); // Get Path to Device pDevPathInternal = pEfiShell->GetDevicePathFromFilePath(pTmpWorkingDir); if (pDevPathInternal == NULL) { ReturnCode = EFI_NOT_FOUND; NVDIMM_ERR("Error: Wrong file path."); goto Finish; } *ppDevicePath = pDevPathInternal; ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pCurDirPath); if (pTmpWorkingDir != NULL) { FreePool(pTmpWorkingDir); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Match driver command status to CLI return code @param[in] Status - NVM_STATUS returned from driver @retval - Appropriate EFI return code **/ EFI_STATUS MatchCliReturnCode( IN NVM_STATUS Status ) { EFI_STATUS ReturnCode = EFI_ABORTED; switch (Status) { case NVM_SUCCESS: case NVM_SUCCESS_IMAGE_EXAMINE_OK: case NVM_SUCCESS_FW_RESET_REQUIRED: case NVM_WARN_BLOCK_MODE_DISABLED: case NVM_WARN_MAPPED_MEM_REDUCED_DUE_TO_CPU_SKU: case NVM_WARN_REGION_MAX_PM_INTERLEAVE_SETS_EXCEEDED: case NVM_WARN_REGION_AD_NI_PM_INTERLEAVE_SETS_REDUCED: case NVM_WARN_GOAL_CREATION_SECURITY_UNLOCKED: case NVM_WARN_NMFM_RATIO_LOWER_VIOLATION_1to3_6: case NVM_WARN_NMFM_RATIO_UPPER_VIOLATION_1to16: case NVM_WARN_NMFM_RATIO_LOWER_VIOLATION_1to2: case NVM_WARN_NMFM_RATIO_UPPER_VIOLATION_1to8: case NVM_WARN_PMEM_MODULE_NOT_PAIRED_FOR_2LM: case NVM_SUCCESS_REQUIRES_POWER_CYCLE: case NVM_WARN_PMTT_TABLE_NOT_FOUND: ReturnCode = EFI_SUCCESS; break; case NVM_ERR_PASSPHRASE_TOO_LONG: case NVM_ERR_NEW_PASSPHRASE_NOT_PROVIDED: case NVM_ERR_PASSPHRASE_NOT_PROVIDED: case NVM_ERR_PASSPHRASES_DO_NOT_MATCH: case NVM_ERR_IMAGE_FILE_NOT_VALID: case NVM_ERR_SENSOR_NOT_VALID: case NVM_ERR_SENSOR_CONTROLLER_TEMP_OUT_OF_RANGE: case NVM_ERR_SENSOR_MEDIA_TEMP_OUT_OF_RANGE: case NVM_ERR_SENSOR_CAPACITY_OUT_OF_RANGE: case NVM_ERR_SENSOR_ENABLED_STATE_INVALID_VALUE: case NVM_ERR_UNSUPPORTED_BLOCK_SIZE: case NVM_ERR_NONE_DIMM_FULFILLS_CRITERIA: case NVM_ERR_INVALID_NAMESPACE_CAPACITY: case NVM_ERR_NAMESPACE_TOO_SMALL_FOR_BTT: case NVM_ERR_REGION_NOT_ENOUGH_SPACE_FOR_PM_NAMESPACE: case NVM_ERR_RESERVE_DIMM_REQUIRES_AT_LEAST_TWO_DIMMS: case NVM_ERR_PERS_MEM_MUST_BE_APPLIED_TO_ALL_DIMMS: case NVM_ERR_INVALID_PARAMETER: ReturnCode = EFI_INVALID_PARAMETER; break; case NVM_ERR_NOT_ENOUGH_FREE_SPACE: case NVM_ERR_NOT_ENOUGH_FREE_SPACE_BTT: ReturnCode = EFI_OUT_OF_RESOURCES; break; case NVM_ERR_DIMM_NOT_FOUND: case NVM_ERR_MANAGEABLE_DIMM_NOT_FOUND: case NVM_ERR_DIMM_EXCLUDED: case NVM_ERR_NO_USABLE_DIMMS: case NVM_ERR_SOCKET_ID_NOT_VALID: case NVM_ERR_REGION_NOT_FOUND: case NVM_ERR_NAMESPACE_DOES_NOT_EXIST: case NVM_ERR_REGION_NO_GOAL_EXISTS_ON_DIMM: ReturnCode = EFI_NOT_FOUND; break; case NVM_ERR_ENABLE_SECURITY_NOT_ALLOWED: case NVM_ERR_CREATE_GOAL_NOT_ALLOWED: case NVM_ERR_INVALID_SECURITY_STATE: case NVM_ERR_INVALID_PASSPHRASE: case NVM_ERR_SPI_ACCESS_NOT_ENABLED: ReturnCode = EFI_ACCESS_DENIED; break; case NVM_ERR_OPERATION_NOT_STARTED: case NVM_ERR_FORCE_REQUIRED: case NVM_ERR_OPERATION_FAILED: case NVM_ERR_DIMM_ID_DUPLICATED: case NVM_ERR_SOCKET_ID_INCOMPATIBLE_W_DIMM_ID: case NVM_ERR_SOCKET_ID_DUPLICATED: case NVM_ERR_UNABLE_TO_GET_SECURITY_STATE: case NVM_ERR_INCONSISTENT_SECURITY_STATE: case NVM_ERR_SECURITY_USER_PP_COUNT_EXPIRED: case NVM_ERR_SECURITY_MASTER_PP_COUNT_EXPIRED: case NVM_ERR_REGION_GOAL_CONF_AFFECTS_UNSPEC_DIMM: case NVM_ERR_REGION_CURR_CONF_AFFECTS_UNSPEC_DIMM: case NVM_ERR_REGION_GOAL_CURR_CONF_AFFECTS_UNSPEC_DIMM: case NVM_ERR_REGION_CONF_APPLYING_FAILED: case NVM_ERR_REGION_CONF_UNSUPPORTED_CONFIG: case NVM_ERR_DUMP_FILE_OPERATION_FAILED: case NVM_ERR_LOAD_VERSION: case NVM_ERR_LOAD_INVALID_DATA_IN_FILE: case NVM_ERR_LOAD_IMPROPER_CONFIG_IN_FILE: case NVM_ERR_LOAD_DIMM_COUNT_MISMATCH: case NVM_ERR_NAMESPACE_CONFIGURATION_BROKEN: case NVM_ERR_INVALID_SECURITY_OPERATION: case NVM_ERR_OPEN_FILE_WITH_WRITE_MODE_FAILED: case NVM_ERR_DUMP_NO_CONFIGURED_DIMMS: case NVM_ERR_REGION_NOT_HEALTHY: case NVM_ERR_FAILED_TO_GET_DIMM_REGISTERS: case NVM_ERR_FAILED_TO_UPDATE_BTT: case NVM_ERR_SMBIOS_DIMM_ENTRY_NOT_FOUND_IN_NFIT: case NVM_ERR_IMAGE_FILE_NOT_COMPATIBLE_TO_CTLR_STEPPING: case NVM_ERR_IMAGE_EXAMINE_INVALID: case NVM_ERR_FIRMWARE_API_NOT_VALID: case NVM_ERR_FIRMWARE_VERSION_NOT_VALID: case NVM_ERR_REGION_GOAL_NAMESPACE_EXISTS: case NVM_ERR_REGION_REMAINING_SIZE_NOT_IN_LAST_PROPERTY: case NVM_ERR_ARS_IN_PROGRESS: case NVM_ERR_FWUPDATE_IN_PROGRESS: case NVM_ERR_OVERWRITE_DIMM_IN_PROGRESS: case NVM_ERR_UNKNOWN_LONG_OP_IN_PROGRESS: case NVM_ERR_APPDIRECT_IN_SYSTEM: case NVM_ERR_OPERATION_NOT_SUPPORTED_BY_MIXED_SKU: case NVM_ERR_SECURE_ERASE_NAMESPACE_EXISTS: case NVM_ERR_CREATE_NAMESPACE_NOT_ALLOWED: ReturnCode = EFI_ABORTED; break; case NVM_ERR_OPERATION_NOT_SUPPORTED: case NVM_ERR_ERROR_INJECTION_BIOS_KNOB_NOT_ENABLED: case NVM_ERR_NMFM_RATIO_GREATER_THAN_ONE: ReturnCode = EFI_UNSUPPORTED; break; case NVM_ERR_FIRMWARE_ALREADY_LOADED: ReturnCode = EFI_ALREADY_STARTED; break; case NVM_ERR_MASTER_PASSPHRASE_NOT_SET: ReturnCode = EFI_NOT_STARTED; break; default: ReturnCode = EFI_ABORTED; break; } return ReturnCode; } /** Get free space of volume from given path @param[in] pFileHandle - file handle protocol @param[out] pFreeSpace - free space @retval - Appropriate EFI return code **/ EFI_STATUS GetVolumeFreeSpace( IN EFI_FILE_HANDLE pFileHandle, OUT UINT64 *pFreeSpace ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_FILE_SYSTEM_INFO *pFileSystemInfo = NULL; EFI_GUID FileSystemInfoGuid = EFI_FILE_SYSTEM_INFO_ID; UINT64 BufferSize = MAX_FILE_SYSTEM_STRUCT_SIZE; NVDIMM_ENTRY(); if (pFreeSpace == NULL || pFileHandle == NULL) { goto Finish; } pFileSystemInfo = AllocateZeroPool(BufferSize); if (pFileSystemInfo == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = pFileHandle->GetInfo(pFileHandle, &FileSystemInfoGuid, &BufferSize, pFileSystemInfo); if (EFI_ERROR(ReturnCode)) { goto Finish; } *pFreeSpace = pFileSystemInfo->FreeSpace; Finish: FREE_POOL_SAFE(pFileSystemInfo); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check if file exists @param[in] pDumpUserPath - destination file path @param[out] pExists - pointer to whether or not destination file already exists @retval - Appropriate EFI return code **/ EFI_STATUS FileExists( IN CHAR16* pDumpUserPath, OUT BOOLEAN* pExists ) { EFI_FILE_HANDLE pFileHandle = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; *pExists = FALSE; NVDIMM_ENTRY(); #ifdef OS_BUILD pFileHandle = NULL; ReturnCode = OpenFileText(pDumpUserPath, &pFileHandle, NULL, FALSE); if (EFI_NOT_FOUND == ReturnCode) { *pExists = FALSE; ReturnCode = EFI_SUCCESS; } else if (EFI_SUCCESS == ReturnCode) { *pExists = TRUE; pFileHandle->Close(pFileHandle); } #else EFI_DEVICE_PATH_PROTOCOL *pDevicePathProtocol = NULL; CHAR16 *pDumpFilePath = NULL; pDumpFilePath = AllocateZeroPool(OPTION_VALUE_LEN * sizeof(*pDumpFilePath)); if (pDumpFilePath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = GetDeviceAndFilePath(pDumpUserPath, pDumpFilePath, &pDevicePathProtocol); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get file path (" FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } ReturnCode = OpenFileByDevice(pDumpFilePath, pDevicePathProtocol, FALSE, &pFileHandle); if (!EFI_ERROR(ReturnCode)) { *pExists = TRUE; pFileHandle->Close(pFileHandle); } Finish: FREE_POOL_SAFE(pDumpFilePath); #endif NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Delete file @param[in] pDumpUserPath - file path to delete @retval - Appropriate EFI return code **/ EFI_STATUS DeleteFile( IN CHAR16* pFilePath ) { EFI_DEVICE_PATH_PROTOCOL *pDevicePathProtocol = NULL; EFI_FILE_HANDLE pFileHandle = NULL; CHAR16 *pDumpFilePath = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_FILE_HANDLE RootDirHandle = NULL; NVDIMM_ENTRY(); pDumpFilePath = AllocateZeroPool(OPTION_VALUE_LEN * sizeof(*pDumpFilePath)); if (pDumpFilePath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = GetDeviceAndFilePath(pFilePath, pDumpFilePath, &pDevicePathProtocol); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get file path (" FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } ReturnCode = OpenRootFileVolume(pDevicePathProtocol, &RootDirHandle); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to open file volume (" FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } ReturnCode = RootDirHandle->Open(RootDirHandle, &pFileHandle, pFilePath, EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (!EFI_ERROR(ReturnCode)) { ReturnCode = pFileHandle->Delete(pFileHandle); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to delete file path (" FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } } Finish: FREE_POOL_SAFE(pDumpFilePath); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Dump data to file @param[in] pDumpUserPath - destination file path @param[in] BufferSize - data size to write @param[in] pBuffer - pointer to buffer @param[in] Overwrite - enforce overwriting file @retval - Appropriate EFI return code **/ EFI_STATUS DumpToFile( IN CHAR16* pDumpUserPath, IN UINT64 BufferSize, OUT VOID* pBuffer, IN BOOLEAN Overwrite ) { #ifdef OS_BUILD EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR8 *path = (CHAR8 *)AllocatePool(StrLen(pDumpUserPath) + 1); if (NULL == path) { NVDIMM_WARN("Failed to allocate enough memory."); return EFI_OUT_OF_RESOURCES; } UnicodeStrToAsciiStrS(pDumpUserPath, path, StrLen(pDumpUserPath) + 1); FILE *destFile = fopen(path, "wb+"); if (NULL == destFile) { NVDIMM_WARN("Failed to open file (%s) errno: (%d)", path, errno); FreePool(path); return EFI_INVALID_PARAMETER; } size_t bytes_written = fwrite(pBuffer, 1, (size_t)BufferSize, destFile); if (bytes_written != BufferSize) { NVDIMM_WARN("Failed to write file (%s) errno: (%d)", path, errno); ReturnCode = EFI_INVALID_PARAMETER; } FreePool(path); fclose(destFile); return ReturnCode; #else EFI_DEVICE_PATH_PROTOCOL *pDevicePathProtocol = NULL; EFI_FILE_HANDLE pFileHandle = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_FILE_HANDLE RootDirHandle = NULL; CHAR16 *pDumpFilePath = NULL; UINT64 FileSize = 0; UINT64 FreeVolumeSpace = 0; UINT64 SizeToWrite = 0; NVDIMM_ENTRY(); if (pDumpUserPath == NULL || pBuffer == NULL) { goto Finish; } pDumpFilePath = AllocateZeroPool(OPTION_VALUE_LEN * sizeof(*pDumpFilePath)); if (pDumpFilePath == NULL) { NVDIMM_CRIT("Out of memory\n"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = GetDeviceAndFilePath(pDumpUserPath, pDumpFilePath, &pDevicePathProtocol); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get file path (" FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } ReturnCode = OpenRootFileVolume(pDevicePathProtocol, &RootDirHandle); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = GetVolumeFreeSpace(RootDirHandle, &FreeVolumeSpace); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (FreeVolumeSpace < BufferSize) { ReturnCode = EFI_VOLUME_FULL; goto Finish; } // Create new file for dump ReturnCode = OpenFileByDevice(pDumpFilePath, pDevicePathProtocol, TRUE, &pFileHandle); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to open file (" FORMAT_EFI_STATUS ") (%s)", ReturnCode, pDumpFilePath); goto Finish; } // Get File Size ReturnCode = GetFileSize(pFileHandle, &FileSize); // Check if file already exists and has some size if (FileSize != 0) { if (Overwrite) { ReturnCode = pFileHandle->Delete(pFileHandle); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed deleting old dump file (" FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } // Create new file for dump ReturnCode = OpenFileByDevice(pDumpFilePath, pDevicePathProtocol, TRUE, &pFileHandle); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to create dump file (" FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } } else { NVDIMM_WARN("File exists and we're not allowed to overwrite (%s)", pDumpFilePath); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } SizeToWrite = BufferSize; ReturnCode = pFileHandle->Write(pFileHandle, &SizeToWrite, pBuffer); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error occurred during write (%s)", pDumpFilePath); goto FinishCloseFile; } FinishCloseFile: ReturnCode = pFileHandle->Close(pFileHandle); Finish: FREE_POOL_SAFE(pDumpFilePath); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; #endif } /** Prints supported or recommended appdirect settings @param[in] pInterleaveFormatList pointer to variable length interleave formats array @param[in] FormatNum number of the appdirect settings formats @param[in] pInterleaveSize pointer to Channel & iMc interleave size, if NULL refer to older revision pInterleaveFormatList @param[in] PrintRecommended if TRUE Recommended settings will be printed if FALSE Supported settings will be printed @param[in] Mode Set mode to print different format @retval String representing AppDirect settings. Null on error. **/ CHAR16* PrintAppDirectSettings( IN VOID *pInterleaveFormatList, IN UINT16 FormatNum, IN INTERLEAVE_SIZE *pInterleaveSize, IN BOOLEAN PrintRecommended, IN UINT8 Mode ) { UINT32 Index = 0; UINT32 Index2 = 0; UINT32 InterleaveWay = 0; ChannelWaysNumber WayNumber = Unknown; InterleaveSizeIndex ImcStringIndex = Unknown; InterleaveSizeIndex ChannelStringIndex = Unknown; UINT8 NumOfBitsSet = 0; UINT8 PrevNumOfBitsSet = 0; BOOLEAN First = TRUE; CHAR16 *pTempBuffer = NULL; UINT32 ChannelInterleaveSize = 0; UINT32 ImcInterleaveSize = 0; UINT16 NumberOfChannelWays = 0; UINT32 Recommended = 0; if (pInterleaveFormatList == NULL) { NVDIMM_CRIT("NULL input parameter.\n"); return NULL; } for (Index = 0; Index < FormatNum; Index++) { if (pInterleaveSize == NULL) { INTERLEAVE_FORMAT *pFormatList = (INTERLEAVE_FORMAT *)pInterleaveFormatList; ChannelInterleaveSize = pFormatList[Index].InterleaveFormatSplit.ChannelInterleaveSize; ImcInterleaveSize = pFormatList[Index].InterleaveFormatSplit.iMCInterleaveSize; NumberOfChannelWays = pFormatList[Index].InterleaveFormatSplit.NumberOfChannelWays & MAX_UINT16; Recommended = pFormatList[Index].InterleaveFormatSplit.Recommended; } else { INTERLEAVE_FORMAT3 *pFormatList = (INTERLEAVE_FORMAT3 *)pInterleaveFormatList; ChannelInterleaveSize = pInterleaveSize->InterleaveSizeSplit.ChannelInterleaveSize; ImcInterleaveSize = pInterleaveSize->InterleaveSizeSplit.iMCInterleaveSize; Recommended = pFormatList[Index].InterleaveFormatSplit.Recommended; CountNumOfBitsSet(pFormatList[Index].InterleaveFormatSplit.InterleaveMap, &NumOfBitsSet); if (NumOfBitsSet == PrevNumOfBitsSet) { continue; } GetBitFieldForNumOfChannelWays(NumOfBitsSet, &NumberOfChannelWays); WayNumber = NumOfBitsSet; PrevNumOfBitsSet = NumOfBitsSet; if (WayNumber == 0) { continue; } } if (PrintRecommended && !Recommended) { continue; } for (Index2 = 0; Index2 < NUMBER_OF_CHANNEL_WAYS_BITS_NUM; Index2++) { /** Check each bit **/ InterleaveWay = NumberOfChannelWays & (1 << Index2); switch (InterleaveWay) { case INTERLEAVE_SET_1_WAY: WayNumber = ChannelWays_X1; break; case INTERLEAVE_SET_2_WAY: WayNumber = ChannelWays_X2; break; case INTERLEAVE_SET_3_WAY: WayNumber = ChannelWays_X3; break; case INTERLEAVE_SET_4_WAY: WayNumber = ChannelWays_X4; break; case INTERLEAVE_SET_6_WAY: WayNumber = ChannelWays_X6; break; case INTERLEAVE_SET_8_WAY: WayNumber = ChannelWays_X8; break; case INTERLEAVE_SET_12_WAY: WayNumber = ChannelWays_X12; break; case INTERLEAVE_SET_16_WAY: WayNumber = ChannelWays_X16; break; case INTERLEAVE_SET_24_WAY: WayNumber = ChannelWays_X24; break; default: WayNumber = Unknown; break; } if (WayNumber == 0) { continue; } switch (ImcInterleaveSize) { case IMC_INTERLEAVE_SIZE_64B: ImcStringIndex = Interleave_64B; break; case IMC_INTERLEAVE_SIZE_128B: ImcStringIndex = Interleave_128B; break; case IMC_INTERLEAVE_SIZE_256B: ImcStringIndex = Interleave_256B; break; case IMC_INTERLEAVE_SIZE_4KB: ImcStringIndex = Interleave_4KB; break; case IMC_INTERLEAVE_SIZE_1GB: ImcStringIndex = Interleave_1GB; break; default: ImcStringIndex = Unknown; break; } switch (ChannelInterleaveSize) { case CHANNEL_INTERLEAVE_SIZE_64B: ChannelStringIndex = Interleave_64B; break; case CHANNEL_INTERLEAVE_SIZE_128B: ChannelStringIndex = Interleave_128B; break; case CHANNEL_INTERLEAVE_SIZE_256B: ChannelStringIndex = Interleave_256B; break; case CHANNEL_INTERLEAVE_SIZE_4KB: ChannelStringIndex = Interleave_4KB; break; case CHANNEL_INTERLEAVE_SIZE_1GB: ChannelStringIndex = Interleave_1GB; break; default: ChannelStringIndex = Unknown; break; } if (ImcStringIndex >= sizeof(mpImcSize)) { ImcStringIndex = 0; } if (ChannelStringIndex >= sizeof(mpChannelSize)) { ChannelStringIndex = 0; } if (!First) { pTempBuffer = CatSPrintClean(pTempBuffer, L", "); } else { First = FALSE; } if (Mode == PRINT_SETTINGS_FORMAT_FOR_SHOW_SYS_CAP_CMD) { if (InterleaveWay == INTERLEAVE_SET_1_WAY) { pTempBuffer = CatSPrintClean(pTempBuffer, L"x1 (ByOne)"); } else { pTempBuffer = CatSPrintClean(pTempBuffer, L"x%d - " FORMAT_STR L" iMC x " FORMAT_STR L" Channel (", WayNumber, mpImcSize[ImcStringIndex], mpChannelSize[ChannelStringIndex]); pTempBuffer = CatSPrintClean(pTempBuffer, FORMAT_STR L"_" FORMAT_STR L")", mpImcSize[ImcStringIndex], mpChannelSize[ChannelStringIndex]); } } else if (Mode == PRINT_SETTINGS_FORMAT_FOR_SHOW_REGION_CMD) { if (InterleaveWay == INTERLEAVE_SET_1_WAY) { pTempBuffer = CatSPrintClean(pTempBuffer, L"x1 (ByOne)"); } else { pTempBuffer = CatSPrintClean(pTempBuffer, L"x%d - " FORMAT_STR L" iMC x " FORMAT_STR L" Channel (" FORMAT_STR L"_" FORMAT_STR L")", WayNumber, mpImcSize[ImcStringIndex], mpChannelSize[ChannelStringIndex], mpImcSize[ImcStringIndex], mpChannelSize[ChannelStringIndex]); } } } } return pTempBuffer; } /** Read source file and return current passphrase to unlock device. @param[in] pCmd A pointer to a COMMAND struct. Used to obtain the Printer context. @param[in] pFileHandle File handler to read Passphrase from @param[in] pDevicePath - handle to obtain generic path/location information concerning the physical device or logical device. The device path describes the location of the device the handle is for. @param[out] ppCurrentPassphrase @param[out] ppNewPassphrase @retval EFI_SUCCESS File load and parse success @retval EFI_INVALID_PARAMETER Invalid Parameter during load @retval other Return Codes from TrimLineBuffer, GetLoadPoolData, GetLoadDimmData, GetLoadValue functions **/ EFI_STATUS ParseSourcePassFile( IN struct Command *pCmd, IN CHAR16 *pFilePath, IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath, OUT CHAR16 **ppCurrentPassphrase OPTIONAL, OUT CHAR16 **ppNewPassphrase OPTIONAL ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CHAR16 *pReadBuffer = NULL; UINT32 Index = 0; UINT32 NumberOfLines = 0; UINT64 FileBufferSize = 0; UINT64 StringLength = 0; CHAR16 **ppLinesBuffer = NULL; CHAR16 *pCurrentLine = NULL; VOID *pFileBuffer = NULL; CHAR16 *pPassFromFile = NULL; CHAR16 *pFileString = NULL; UINT32 NumberOfChars = 0; BOOLEAN PassphraseProvided = FALSE; BOOLEAN NewPassphraseProvided = FALSE; BOOLEAN TextFallThrough = TRUE; NVDIMM_ENTRY(); #ifndef OS_BUILD if (pDevicePath == NULL || pCmd == NULL) { NVDIMM_CRIT("NULL input parameter.\n"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } #endif if (pFilePath == NULL || pCmd == NULL) { NVDIMM_CRIT("NULL input parameter.\n"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = FileRead(pFilePath, pDevicePath, MAX_CONFIG_DUMP_FILE_SIZE, FALSE, &FileBufferSize, (VOID **)&pFileBuffer); if (EFI_ERROR(ReturnCode) || pFileBuffer == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_WRONG_FILE_PATH); goto Finish; } // Verify if it is Unicode file: //If it is not a Unicode File Convert the File String if (*((CHAR16 *)pFileBuffer) != UTF_16_BOM) { pFileString = AllocateZeroPool((FileBufferSize * sizeof(CHAR16)) + sizeof(L'\0')); if (pFileString == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = SafeAsciiStrToUnicodeStr((const CHAR8 *)pFileBuffer, (UINT32)FileBufferSize, pFileString); Index = 0; FREE_POOL_SAFE(pFileBuffer); } else { // Add size of L'\0' (UTF16) char // ReallocatePool frees pFileBuffer after completion. Do not need to call FREE_POOL_SAFE for pFileBuffer pFileString = ReallocatePool(FileBufferSize, FileBufferSize + sizeof(L'\0'), pFileBuffer); if (pFileString == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } Index = 1; NumberOfChars = (UINT32)(FileBufferSize / sizeof(CHAR16)); pFileString[NumberOfChars] = L'\0'; } // Split input file to lines ppLinesBuffer = StrSplit(&pFileString[Index], L'\n', &NumberOfLines); if (ppLinesBuffer == NULL || NumberOfLines == 0) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, L"Error: The file is empty.\n"); goto Finish; } for (Index = 0; Index < NumberOfLines; ++Index) { pCurrentLine = ppLinesBuffer[Index]; StringLength = StrLen(pCurrentLine); // Ignore comment line that starts with '#' or // If the only content in line is new line chars if ( ((NULL == pCurrentLine) || (L'#' == pCurrentLine[0])) || (1 == StringLength && (L'\n' == pCurrentLine[0] || L'\r' == pCurrentLine[0])) || (2 == StringLength && L'\r' == pCurrentLine[0] && L'\n' == pCurrentLine[1])) { continue; } else { TextFallThrough = FALSE; } pPassFromFile = (CHAR16*)StrStr(ppLinesBuffer[Index], L"="); if (pPassFromFile == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INVALID_PASSPHRASE_FROM_FILE); goto Finish; } // Move offset to skip '=' char pPassFromFile++; StringLength = StrLen(pPassFromFile); if (StringLength == 0) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INVALID_PASSPHRASE_FROM_FILE); goto Finish; } // Cut off new line chars present at the end while ((1 <= StringLength) && (L'\r' == pPassFromFile[StringLength - 1] || L'\n' == pPassFromFile[StringLength - 1])) { pPassFromFile[StringLength - 1] = L'\0'; StringLength--; } NewPassphraseProvided = StrnCmp(ppLinesBuffer[Index], NEWPASSPHRASE_PROPERTY, StrLen(NEWPASSPHRASE_PROPERTY)) == 0; PassphraseProvided = StrnCmp(ppLinesBuffer[Index], PASSPHRASE_PROPERTY, StrLen(PASSPHRASE_PROPERTY)) == 0; if (ppNewPassphrase != NULL && *ppNewPassphrase == NULL && NewPassphraseProvided) { *ppNewPassphrase = CatSPrint(NULL, FORMAT_STR, pPassFromFile); } else if (ppCurrentPassphrase != NULL && *ppCurrentPassphrase == NULL && PassphraseProvided) { *ppCurrentPassphrase = CatSPrint(NULL, FORMAT_STR, pPassFromFile); } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_WRONG_FILE_DATA); goto Finish; } } //In case the file has only comments and new line if (TRUE == TextFallThrough) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_WRONG_FILE_DATA); } Finish: for (Index = 0; ppLinesBuffer != NULL && Index < NumberOfLines; ++Index) { FREE_POOL_SAFE(ppLinesBuffer[Index]); } FREE_POOL_SAFE(pFileString); FREE_POOL_SAFE(ppLinesBuffer); FREE_POOL_SAFE(pReadBuffer); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #ifndef OS_BUILD /** Prompted input request @param[in] pPrompt - information about expected input @param[in] ShowInput - Show characters written by user @param[in] OnlyAlphanumeric - Allow only for alphanumeric characters @param[out] ppReturnValue - is a pointer to a pointer to the 16-bit character string that will contain the return value @retval - Appropriate CLI return code **/ EFI_STATUS PromptedInput( IN CHAR16 *pPrompt, IN BOOLEAN ShowInput, IN BOOLEAN OnlyAlphanumeric, OUT CHAR16 **ppReturnValue ) { CHAR16 *pBuffer = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pPrompt == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } Print(FORMAT_STR, pPrompt); ReturnCode = ConsoleInput(ShowInput, OnlyAlphanumeric, &pBuffer, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *ppReturnValue = pBuffer; Finish: Print(L"\n"); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Display "yes/no" question and retrieve reply using prompt mechanism @param[out] pConfirmation Confirmation from prompt @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All Ok **/ EFI_STATUS PromptYesNo( OUT BOOLEAN *pConfirmation ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CHAR16 *pPromptReply = NULL; BOOLEAN ValidInput = FALSE; NVDIMM_ENTRY(); if (pConfirmation == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = PromptedInput(PROMPT_CONTINUE_QUESTION, TRUE, TRUE, &pPromptReply); if ((NULL == pPromptReply) || (EFI_ERROR(ReturnCode))) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ValidInput = StrLen(pPromptReply) == 1 && (StrICmp(pPromptReply, L"y") == 0 || StrICmp(pPromptReply, L"n") == 0); if (EFI_ERROR(ReturnCode) || !ValidInput) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (StrICmp(pPromptReply, L"y") == 0) { *pConfirmation = TRUE; } else { *pConfirmation = FALSE; } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pPromptReply); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif /** Read input from console @param[in] ShowInput - Show characters written by user @param[in] OnlyAlphanumeric - Allow only for alphanumeric characters @param[in, out] ppReturnValue - is a pointer to a pointer to the 16-bit character string without null-terminator that will contain the return value @param[in, out] pBufferSize - is a pointer to the Size in bytes of the return buffer @retval - Appropriate CLI return code **/ EFI_STATUS ConsoleInput( IN BOOLEAN ShowInput, IN BOOLEAN OnlyAlphanumeric, IN OUT CHAR16 **ppReturnValue, IN OUT UINTN *pBufferSize OPTIONAL ) { EFI_INPUT_KEY Key = { 0 }; UINTN SizeInBytes = 0; CHAR16 *pBuffer = NULL; UINTN EventIndex = 0; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (ppReturnValue == NULL) { goto Finish; } while (1) { gBS->WaitForEvent(1, &gST->ConIn->WaitForKey, &EventIndex); ReturnCode = gST->ConIn->ReadKeyStroke(gST->ConIn, &Key); if (EFI_ERROR(ReturnCode)) { Print(L"Error reading key strokes.\n"); goto Finish; } if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { if (pBuffer == NULL || StrLen(pBuffer) == 0 || SizeInBytes <= 0) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } else { *ppReturnValue = pBuffer; if (pBufferSize != NULL) { *pBufferSize = SizeInBytes; } break; } } if ((SizeInBytes != 0 && pBuffer == NULL) || (SizeInBytes == 0 && pBuffer != NULL)) { ReturnCode = EFI_BAD_BUFFER_SIZE; goto Finish; } if (Key.UnicodeChar == CHAR_BACKSPACE) { if (pBuffer != NULL && StrLen(pBuffer) > 0) { pBuffer[StrLen(pBuffer) - 1] = L'\0'; if (ShowInput) { Print(L"%c", Key.UnicodeChar); } } } else { if (!OnlyAlphanumeric || IsUnicodeAlnumCharacter(Key.UnicodeChar)) { StrnCatGrow(&pBuffer, &SizeInBytes, &Key.UnicodeChar, 1); if (NULL == pBuffer) { Print(L"Failure inputting characters.\n"); break; } if (ShowInput) { Print(L"%c", Key.UnicodeChar); } } } } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Print Load Firmware progress for all DIMMs @param[in] ProgressEvent EFI Event @param[in] pContext context pointer **/ VOID EFIAPI PrintProgress( IN EFI_EVENT ProgressEvent, IN VOID *pContext ) { OBJECT_STATUS *pObjectStatus = NULL; LIST_ENTRY *pObjectStatusNode = NULL; STATIC UINT32 LastObjectId = 0; COMMAND_STATUS *pCommandStatus = NULL; /** For reuse of this function one should do one of two things: 1) Add string pointer to COMMAND_STATUS and pass it to Print instead of current define 2) Use some other structure instead of COMMAND_STATUS **/ if (pContext == NULL) { goto Finish; } pCommandStatus = (COMMAND_STATUS*)pContext; if (!IsListInitialized(pCommandStatus->ObjectStatusList) && !IsListEmpty(&pCommandStatus->ObjectStatusList)) { goto Finish; } LIST_FOR_EACH(pObjectStatusNode, &pCommandStatus->ObjectStatusList) { pObjectStatus = OBJECT_STATUS_FROM_NODE(pObjectStatusNode); if (IsSetNvmStatus(pObjectStatus, NVM_OPERATION_IN_PROGRESS)) { if (LastObjectId == 0) { LastObjectId = pObjectStatus->ObjectId; } else if (LastObjectId != pObjectStatus->ObjectId) { Print(L"\n"); LastObjectId = pObjectStatus->ObjectId; } Print(CLI_PROGRESS_STR, pObjectStatus->ObjectId, pObjectStatus->Progress); break; } } Finish: return; } /** Get relative path from absolute path. Output pointer points to the same string as input but with necessary offset. Caller shall not free it. @param[in] pAbsolutePath Absolute path @param[out] ppRelativePath Relative path @retval EFI_INVALID_PARAMETER Input parameter was NULL @retval EFI_SUCCESS All Ok **/ EFI_STATUS GetRelativePath( IN CHAR16 *pAbsolutePath, OUT CHAR16 **ppRelativePath ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; if (pAbsolutePath == NULL) { goto Finish; } *ppRelativePath = pAbsolutePath; if (ContainsCharacter(':', *ppRelativePath)) { while (*ppRelativePath[0] != '\\' && *ppRelativePath[0] != '\0') { (*ppRelativePath)++; } } ReturnCode = EFI_SUCCESS; Finish: return ReturnCode; } /** Check if all dimms in the specified pDimmIds list are manageable. This helper method assumes all the dimms in the list exist. This helper method also assumes the parameters are non-null. @param[in] pAllDimms The dimm list found in NFIT @param[in] AllDimmCount Size of the pAllDimms array @param[in] pDimmsListToCheck Pointer to the array of DimmIDs to check @param[in] DimmsToCheckCount Size of the pDimmsListToCheck array @retval TRUE if all Dimms in pDimmsListToCheck array are manageable @retval FALSE if at least one DIMM is not manageable **/ BOOLEAN AllDimmsInListAreManageable( IN DIMM_INFO *pAllDimms, IN UINT32 AllDimmCount, IN UINT16 *pDimmsListToCheck, IN UINT32 DimmsToCheckCount ) { BOOLEAN Manageable = TRUE; UINT32 AllDimmListIndex = 0; UINT32 DimmsToCheckIndex = 0; NVDIMM_ENTRY(); for (DimmsToCheckIndex = 0; DimmsToCheckIndex < DimmsToCheckCount; DimmsToCheckIndex++) { for (AllDimmListIndex = 0; AllDimmListIndex < AllDimmCount; AllDimmListIndex++) { if (pAllDimms[AllDimmListIndex].DimmID == pDimmsListToCheck[DimmsToCheckIndex]) { if (pAllDimms[AllDimmListIndex].ManageabilityState != MANAGEMENT_VALID_CONFIG) { Manageable = FALSE; break; } } } } NVDIMM_EXIT(); return Manageable; } /** Check if all dimms in the specified pDimmIds list are in supported config. This helper method assumes all the dimms in the list exist. This helper method also assumes the parameters are non-null. @param[in] pAllDimms The dimm list found in NFIT @param[in] AllDimmCount Size of the pAllDimms array @param[in] pDimmsListToCheck Pointer to the array of DimmIDs to check @param[in] DimmsToCheckCount Size of the pDimmsListToCheck array @retval TRUE if all Dimms in pDimmsListToCheck array are in supported config @retval FALSE if at least one DIMM is not in supported config **/ BOOLEAN AllDimmsInListInSupportedConfig( IN DIMM_INFO *pAllDimms, IN UINT32 AllDimmCount, IN UINT16 *pDimmsListToCheck, IN UINT32 DimmsToCheckCount ) { BOOLEAN InSupportedConfig = TRUE; UINT32 AllDimmListIndex = 0; UINT32 DimmsToCheckIndex = 0; NVDIMM_ENTRY(); for (DimmsToCheckIndex = 0; DimmsToCheckIndex < DimmsToCheckCount; DimmsToCheckIndex++) { for (AllDimmListIndex = 0; AllDimmListIndex < AllDimmCount; AllDimmListIndex++) { if (pAllDimms[AllDimmListIndex].DimmID == pDimmsListToCheck[DimmsToCheckIndex]) { if (pAllDimms[AllDimmListIndex].IsInPopulationViolation == TRUE) { InSupportedConfig = FALSE; break; } } } } NVDIMM_EXIT(); return InSupportedConfig; } /** Check if all dimms in the specified pDimmIds list have master passphrase enabled. This helper method assumes all the dimms in the list exist. This helper method also assumes the parameters are non-null. @param[in] pAllDimms The dimm list found in NFIT @param[in] AllDimmCount Size of the pAllDimms array @param[in] pDimmsListToCheck Pointer to the array of DimmIDs to check @param[in] DimmsToCheckCount Size of the pDimmsListToCheck array @param[in] SkipFIS32Dimms indicates that Dimms with FIS 3.2 or above should always pass @retval TRUE if all Dimms in pDimmsListToCheck array have master passphrase enabled @retval FALSE if at least one DIMM does not have master passphrase enabled **/ BOOLEAN AllDimmsInListHaveMasterPassphraseEnabled( IN DIMM_INFO *pAllDimms, IN UINT32 AllDimmCount, IN UINT16 *pDimmsListToCheck, IN UINT32 DimmsToCheckCount, IN BOOLEAN SkipFIS32Dimms ) { BOOLEAN MasterPassphraseEnabled = FALSE; UINT32 AllDimmListIndex = 0; UINT32 DimmsToCheckIndex = 0; BOOLEAN DimmFound = FALSE; NVDIMM_ENTRY(); if (pAllDimms == NULL || pDimmsListToCheck == NULL || AllDimmCount == 0 || DimmsToCheckCount == 0) { NVDIMM_DBG("Invalid parameter."); goto Finish; } for (DimmsToCheckIndex = 0; DimmsToCheckIndex < DimmsToCheckCount; DimmsToCheckIndex++) { DimmFound = FALSE; for (AllDimmListIndex = 0; AllDimmListIndex < AllDimmCount; AllDimmListIndex++) { if (pAllDimms[AllDimmListIndex].DimmID == pDimmsListToCheck[DimmsToCheckIndex]) { DimmFound = TRUE; if (pAllDimms[AllDimmListIndex].MasterPassphraseEnabled == FALSE) { // starting in FIS 3.2, MasterPassphraseEnabled shows as false until the passphrase is changed from the default if (SkipFIS32Dimms) { if (((3 == pAllDimms[AllDimmListIndex].FwVer.FwApiMajor) && (2 > pAllDimms[AllDimmListIndex].FwVer.FwApiMinor)) || (2 >= pAllDimms[AllDimmListIndex].FwVer.FwApiMajor)) { goto Finish; } } else { goto Finish; } } } } if (!DimmFound) { NVDIMM_DBG("DimmID: 0x%04x not found.", pDimmsListToCheck[DimmsToCheckIndex]); goto Finish; } } MasterPassphraseEnabled = TRUE; Finish: NVDIMM_EXIT(); return MasterPassphraseEnabled; } /** Retrieve the User Cli Display Preferences CMD line arguments. @param[out] pDisplayPreferences pointer to the current driver preferences. @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS ReadCmdLinePrintOptions( IN OUT PRINT_FORMAT_TYPE *pFormatType, IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 *OutputOptions = NULL; CHAR16 **Toks = NULL; UINT32 NumToks = 0; UINT32 Index = 0; if (NULL == pFormatType) { return EFI_INVALID_PARAMETER; } if (NULL == (OutputOptions = getOptionValue(pCmd, OUTPUT_OPTION_SHORT))) { if (NULL == (OutputOptions = getOptionValue(pCmd, OUTPUT_OPTION))) { *pFormatType = TEXT; PRINTER_ENABLE_LIST_TABLE_FORMAT(pCmd->pPrintCtx); return ReturnCode; } } *pFormatType = TEXT; //default if (NULL != (Toks = StrSplit(OutputOptions, L',', &NumToks))) { for (Index = 0; Index < NumToks; ++Index) { if (0 == StrICmp(Toks[Index], OUTPUT_OPTION_TEXT)) { *pFormatType = TEXT; PRINTER_ENABLE_LIST_TABLE_FORMAT(pCmd->pPrintCtx); // default for TEXT } else if (0 == StrICmp(Toks[Index], OUTPUT_OPTION_NVMXML)) { *pFormatType = XML; } else if (0 == StrICmp(Toks[Index], OUTPUT_OPTION_ESX_XML)) { *pFormatType = XML; PRINTER_ENABLE_ESX_XML_FORMAT(pCmd->pPrintCtx); } else if (0 == StrICmp(Toks[Index], OUTPUT_OPTION_ESX_TABLE_XML)) { *pFormatType = XML; PRINTER_ENABLE_ESX_TABLE_XML_FORMAT(pCmd->pPrintCtx); } else { // Print out syntax specific help message for invalid -output option CHAR16 * pHelpStr = getCommandHelp(pCmd, TRUE); CHAR16 *pSyntaxTokStr = CatSPrint(NULL, CLI_PARSER_ERR_UNEXPECTED_TOKEN, Toks[Index]); if (NULL != pHelpStr) { CHAR16 *pSyntaxHelp = CatSPrintClean(pSyntaxTokStr, FORMAT_NL_STR FORMAT_NL_STR, CLI_PARSER_DID_YOU_MEAN, pHelpStr); LongPrint(pSyntaxHelp); FREE_POOL_SAFE(pSyntaxHelp); } else { // in case the command is bad, try to print something helpful. LongPrint(pSyntaxTokStr); FREE_POOL_SAFE(pSyntaxTokStr); } FREE_POOL_SAFE(pHelpStr); ReturnCode = EFI_INVALID_PARAMETER; } } } FREE_POOL_SAFE(OutputOptions); FreeStringArray(Toks, NumToks); FREE_POOL_SAFE(OutputOptions); return ReturnCode; } /** Helper to recreate -o args in string format @param[in] pCmd command from CLI @param[out] ppOutputStr resulting -o string @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd or ppOutputStr is NULL **/ EFI_STATUS CreateCmdLineOutputStr( IN struct Command *pCmd, OUT CHAR16 **ppOutputStr ) { if (NULL == pCmd || NULL == ppOutputStr) { return EFI_INVALID_PARAMETER; } if (XML != pCmd->pPrintCtx->FormatType) { *ppOutputStr = CatSPrint(NULL, L""); return EFI_SUCCESS; } *ppOutputStr = CatSPrint(*ppOutputStr, OUTPUT_OPTION_SHORT L" "); if (pCmd->pPrintCtx->FormatTypeFlags.Flags.EsxCustom) { *ppOutputStr = CatSPrintClean(*ppOutputStr, OUTPUT_OPTION_ESX_TABLE_XML L" "); } else if (pCmd->pPrintCtx->FormatTypeFlags.Flags.EsxKeyVal) { *ppOutputStr = CatSPrintClean(*ppOutputStr, OUTPUT_OPTION_ESX_XML L" "); } else { *ppOutputStr = CatSPrintClean(*ppOutputStr, OUTPUT_OPTION_NVMXML L" "); } return EFI_SUCCESS; } /** Get Dimm identifier preference @param[out] pDimmIdentifier Variable to store Dimm identifier preference @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS GetDimmIdentifierPreference( OUT UINT8 *pDimmIdentifier ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DISPLAY_PREFERENCES DisplayPreferences; NVDIMM_ENTRY(); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); if (pDimmIdentifier == NULL) { goto Finish; } ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { Print(FORMAT_STR_NL, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } *pDimmIdentifier = DisplayPreferences.DimmIdentifier; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get Dimm identifier as string based on user preference @param[in] DimmId Dimm ID as number @param[in] pDimmUid Dimm UID as string @param[out] pResultString String representation of preferred value @param[in] ResultStringLen Length of pResultString @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS GetPreferredDimmIdAsString( IN UINT32 DimmId, IN CHAR16 *pDimmUid OPTIONAL, OUT CHAR16 *pResultString, IN UINT32 ResultStringLen ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT8 DimmIdentifier = 0; NVDIMM_ENTRY(); if (pResultString == NULL) { goto Finish; } ReturnCode = GetDimmIdentifierPreference(&DimmIdentifier); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = GetPreferredValueAsString(DimmId, pDimmUid, DimmIdentifier == DISPLAY_DIMM_ID_HANDLE, pResultString, ResultStringLen); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve Display DimmID Runtime Index from Property String @param[in] String to try to discover index for @retval DimmID Index of DimmID property string @retval Size of Array if not found **/ UINT8 GetDimmIDIndex( IN CHAR16 *pDimmIDStr ) { UINT8 Index = 0; for (Index = 0; Index < DISPLAY_DIMM_ID_MAX_SIZE; Index++) { if (StrICmp(pDimmIDStr, mpDefaultDimmIds[Index]) == 0) { break; } } return Index; } /** Retrieve Display Size Runtime Index from Property String @param[in] String to try to discover index for @retval Display Size Index of Size property string @retval Size of Array if not found **/ UINT8 GetDisplaySizeIndex( IN CHAR16 *pSizeStr ) { UINT8 Index = 0; for (Index = 0; Index < DISPLAY_SIZE_MAX_SIZE; Index++) { if (StrICmp(pSizeStr, mpDefaultSizeStrs[Index]) == 0) { break; } } return Index; } /** Retrieve Display DimmID String from RunTime variable index @param[in] Index to retrieve @retval NULL Index was invalid @retval DimmID String of user display preference **/ CONST CHAR16 *GetDimmIDStr( IN UINT8 DimmIDIndex ) { if (DimmIDIndex >= DISPLAY_DIMM_ID_MAX_SIZE) { return NULL; } return mpDefaultDimmIds[DimmIDIndex]; } /** Retrieve Display Size String from RunTime variable index @param[in] Index to retrieve @retval NULL Index was invalid @retval Size String of user display preference **/ CONST CHAR16 *GetDisplaySizeStr( IN UINT16 DisplaySizeIndex ) { if (DisplaySizeIndex >= DISPLAY_SIZE_MAX_SIZE) { return NULL; } return mpDefaultSizeStrs[DisplaySizeIndex]; } /** Allocate and return string which is related with the binary RegionType value. The caller function is obligated to free memory of the returned string. @param[in] RegionType - region type @retval - output string **/ CHAR16 * RegionTypeToString( IN UINT8 RegionType ) { CHAR16 *pRegionTypeString = NULL; if ((RegionType & PM_TYPE_AD) != 0) { pRegionTypeString = CatSPrintClean(pRegionTypeString, FORMAT_STR, PERSISTENT_MEM_TYPE_AD_STR); } if ((RegionType & PM_TYPE_AD_NI) != 0) { pRegionTypeString = CatSPrintClean(pRegionTypeString, FORMAT_STR FORMAT_STR, pRegionTypeString == NULL ? L"" : L", ", PERSISTENT_MEM_TYPE_AD_NI_STR); } return pRegionTypeString; } /** Gets the DIMM handle corresponding to Dimm PID and also the index @param[in] DimmId - DIMM ID @param[in] pDimms - List of DIMMs @param[in] DimmsNum - Number of DIMMs @param[out] pDimmHandle - The Dimm Handle corresponding to the DIMM ID @param[out] pDimmIndex - The Index of the found DIMM @retval - EFI_STATUS Success @retval - EFI_INVALID_PARAMETER Invalid parameter @retval - EFI_NOT_FOUND Dimm not found **/ EFI_STATUS GetDimmHandleByPid( IN UINT16 DimmId, IN DIMM_INFO *pDimms, IN UINT32 DimmsNum, OUT UINT32 *pDimmHandle, OUT UINT32 *pDimmIndex ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM_INFO *pFoundDimm = NULL; UINT32 Index = 0; NVDIMM_ENTRY(); if (pDimms == NULL || pDimmHandle == NULL || pDimmIndex == NULL) { goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { if (pDimms[Index].DimmID == DimmId) { pFoundDimm = &pDimms[Index]; *pDimmIndex = Index; break; } } if (pFoundDimm == NULL) { ReturnCode = EFI_NOT_FOUND; goto Finish; } *pDimmHandle = pFoundDimm->DimmHandle; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Convert UEFI return codes to legacy OS return codes @param[in] UefiReturnCode - return code to Convert @retval - Converted OS ReturnCode **/ EFI_STATUS UefiToOsReturnCode(EFI_STATUS UefiReturnCode) { EFI_STATUS ReturnCode = EFI_SUCCESS; switch (UefiReturnCode) { case (0): break; case (2): ReturnCode = 201; break; case (EFI_INVALID_PARAMETER): ReturnCode = 201; break; case (EFI_ALREADY_STARTED): //this number is arbitrary, but should be distinct. //In the case of FW update, it indicates that all DIMMs //have a staged FW binary ReturnCode = 20; break; default: ReturnCode = 1; } return ReturnCode; } /** Checks if user has incorrectly used master and default options. Also checks for invalid combinations of these options with the Passphrase property. @param[in] pCmd command from CLI @param[in] isPassphraseProvided TRUE if user provided passphrase @param[in] isMasterOptionSpecified TRUE if master option is specified @param[in] isDefaultOptionSpecified TRUE if default option is specified @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS CheckMasterAndDefaultOptions( IN struct Command *pCmd, IN BOOLEAN isPassphraseProvided, IN BOOLEAN isMasterOptionSpecified, IN BOOLEAN isDefaultOptionSpecified ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PRINT_CONTEXT *pPrinterCtx = NULL; NVDIMM_ENTRY(); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; if (isPassphraseProvided) { if (isMasterOptionSpecified && isDefaultOptionSpecified) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DEFAULT_OPTION_PASSPHRASE_PROPERTY_USED_TOGETHER); goto Finish; } else if (!isMasterOptionSpecified && isDefaultOptionSpecified) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DEFAULT_OPTION_NOT_COMBINED); goto Finish; } } else { // Passphrase not provided if (isMasterOptionSpecified && !isDefaultOptionSpecified) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_MISSING_PASSPHRASE_PROPERTY); goto Finish; } else if (!isMasterOptionSpecified && isDefaultOptionSpecified) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DEFAULT_OPTION_NOT_COMBINED); goto Finish; } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieves a list of Dimms that have at least one NS. @param[in,out] pDimmIds the dimm IDs which have NS @param[in,out] pDimmIdCount count of dimm IDs @param[in] maxElements the maximum size of the dimm ID list @retval EFI_ABORTED Operation Aborted @retval EFI_OUT_OF_RESOURCES unable to allocate memory @retval EFI_SUCCESS All Ok **/ EFI_STATUS GetDimmIdsWithNamespaces( IN OUT UINT16 *pDimmIds, IN OUT UINT32 *pDimmIdCount, IN UINT32 maxElements) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; COMMAND_STATUS *pCommandStatus = NULL; UINT32 NamespacesCount = 0; LIST_ENTRY NamespaceListHead; NAMESPACE_INFO *pNamespaceInfo = NULL; LIST_ENTRY *pCurNamespace = NULL; UINT32 RegionIndex = 0; UINT32 Index = 0; UINT32 RegionCount = 0; REGION_INFO *pRegions = NULL; LIST_ENTRY *pTmpListNode = NULL; LIST_ENTRY *pTmpListNextNode = NULL; NVDIMM_ENTRY(); ZeroMem(&NamespaceListHead, sizeof(NamespaceListHead)); InitializeListHead(&NamespaceListHead); ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_ABORTED; NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID**)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } /* Load Regions */ ReturnCode = pNvmDimmConfigProtocol->GetRegionCount(pNvmDimmConfigProtocol, FALSE, &RegionCount); if (EFI_ERROR(ReturnCode)) { if (EFI_NO_RESPONSE == ReturnCode) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); } ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); goto Finish; } pRegions = AllocateZeroPool(sizeof(REGION_INFO) * RegionCount); if (pRegions == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetRegions(pNvmDimmConfigProtocol, RegionCount, FALSE, pRegions, pCommandStatus); if (EFI_ERROR(ReturnCode)) { if (pCommandStatus->GeneralStatus != NVM_SUCCESS) { ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); } else { ReturnCode = EFI_ABORTED; } NVDIMM_WARN("Failed to retrieve the REGION list"); goto Finish; } /*Load Namespaces*/ ReturnCode = pNvmDimmConfigProtocol->GetNamespaces(pNvmDimmConfigProtocol, &NamespaceListHead, &NamespacesCount, pCommandStatus); if (EFI_ERROR(ReturnCode)) { if (pCommandStatus->GeneralStatus != NVM_SUCCESS) { ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); } NVDIMM_WARN("Failed to retrieve Namespaces list"); goto Finish; } for (RegionIndex = 0; RegionIndex < RegionCount; RegionIndex++) { LIST_FOR_EACH(pCurNamespace, &NamespaceListHead) { pNamespaceInfo = NAMESPACE_INFO_FROM_NODE(pCurNamespace); if (pNamespaceInfo->RegionId == pRegions[RegionIndex].RegionId) { //add the DIMM id to the main return list for (Index = 0; Index < pRegions[RegionIndex].DimmIdCount; Index++) { ReturnCode = AddElement(pDimmIds, pDimmIdCount, pRegions[RegionIndex].DimmId[Index], maxElements); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to add the DIMM ID to the list"); goto Finish; } } } } } Finish: NVDIMM_EXIT_I64(ReturnCode); FREE_POOL_SAFE(pRegions); LIST_FOR_EACH_SAFE(pTmpListNode, pTmpListNextNode, &NamespaceListHead) { FreePool(NAMESPACE_INFO_FROM_NODE(pTmpListNode)); } FreeCommandStatus(&pCommandStatus); return ReturnCode; } /** Adds an element to a element list without allowing duplication @param[in,out] pElementList the list @param[in,out] pElementCount size of the list @param[in] newElement the new element to add @param[in] maxElements the maximum size of the list @retval EFI_OUT_OF_RESOURCES unable to add any more elements @retval EFI_SUCCESS All Ok **/ EFI_STATUS AddElement( IN OUT UINT16 *pElementList, IN OUT UINT32 *pElementCount, IN UINT16 newElement, IN UINT32 maxElements) { UINT32 x = 0; //check for initial condition if (pElementList == NULL || pElementCount == NULL) { return EFI_SUCCESS; } //see if the list already has this item for (; x < *pElementCount && x < maxElements; x++) { if (pElementList[x] == newElement) { return EFI_SUCCESS; } } if (x == maxElements) { return EFI_OUT_OF_RESOURCES; } *pElementCount = (*pElementCount) + 1; pElementList[x] = newElement; return EFI_SUCCESS; } /** Checks whether the FW on the dimm restricts executing commands with the default Master Passphrase. The restriction is implemented in API version 3.2. @param[in] DimmInfo is the information about the dimm to check @retval whether default is restricted by the FW's API version **/ BOOLEAN IsDefaultMasterPassphraseRestricted( IN DIMM_INFO DimmInfo) { if (((3 == DimmInfo.FwVer.FwApiMajor) && (2 <= DimmInfo.FwVer.FwApiMinor)) || (4 <= DimmInfo.FwVer.FwApiMajor)) { return TRUE; } else { return FALSE; } }ipmctl-03.00.00.0485/DcpmPkg/cli/Common.h000066400000000000000000001343561440615110200173240ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _COMMON_H_ #define _COMMON_H_ #include #include "CommandParser.h" #include "NvmStatus.h" extern OBJECT_STATUS gAllErrorNvmStatuses; extern OBJECT_STATUS gAllWarningNvmStatuses; extern EFI_GUID gNvmDimmConfigProtocolGuid; extern EFI_GUID gNvmDimmPbrProtocolGuid; typedef struct _CMD_DISPLAY_OPTIONS { BOOLEAN DisplayOptionSet; BOOLEAN AllOptionSet; CHAR16 *pDisplayValues; }CMD_DISPLAY_OPTIONS; /** common display options **/ #define SOCKET_ID_STR L"SocketID" #define DIE_ID_STR L"DieID" #define DIMM_ID_STR L"DimmID" #define TAG_ID_STR L"TagID" #define EXIT_CODE_STR L"RC" #define CLI_ARGS_STR L"Args" #define TAG_STR L"Tag" #define DDR_STR L"DDR" // Find PMEM_MODULE_STR in CommandParser.h #define TOTAL_STR L"Total" #define MEMORY_TYPE_STR L"MemoryType" /** common display values**/ #define NA_STR L"N/A" #define DASH_STR L"-" #define MAX_FILE_PATH_LEN 512 #define MAX_FILE_SYSTEM_STRUCT_SIZE BLOCKSIZE_4K #define MAX_SHELL_PROTOCOL_HANDLES 2 #define PROGRESS_EVENT_TIMEOUT EFI_TIMER_PERIOD_SECONDS(1) #define PRINT_PRIORITY 8 // FW log level string values #define FW_LOG_LEVEL_DISABLED_STR L"Disabled" #define FW_LOG_LEVEL_ERROR_STR L"Error" #define FW_LOG_LEVEL_WARNING_STR L"Warning" #define FW_LOG_LEVEL_INFO_STR L"Info" #define FW_LOG_LEVEL_DEBUG_STR L"Debug" #define FW_LOG_LEVEL_UNKNOWN_STR L"Unknown" // Parser CLI messages #define CLI_PARSER_DID_YOU_MEAN L"Did you mean:" #define CLI_PARSER_ERR_UNEXPECTED_TOKEN L"Syntax Error: Invalid or unexpected token " FORMAT_STR L"." #define CLI_PARSER_ERR_INVALID_COMMAND L"Syntax Error: Invalid or unsupported command." #define CLI_PARSER_ERR_INVALID_OPTION_VALUES L"Syntax Error: Invalid option values input." #define CLI_PARSER_ERR_MUTUALLY_EXCLUSIVE_OPTIONS L"Syntax Error: Mutually exclusive options " FORMAT_STR L" and " FORMAT_STR L"." #define CLI_PARSER_ERR_INVALID_TARGET_VALUES L"Syntax Error: Invalid target values input." #define CLI_PARSER_ERR_VERB_EXPECTED L"Syntax Error: First token must be a verb, '" FORMAT_STR L"' is not a supported verb." #define CLI_PARSER_DETAILED_ERR_OPTION_VALUE_REQUIRED L"The option " FORMAT_STR L" requires a value." #define CLI_PARSER_DETAILED_ERR_OPTION_VALUE_UNEXPECTED L"The option " FORMAT_STR L" does not accept a value." #define CLI_PARSER_DETAILED_ERR_TARGET_VALUE_REQUIRED L"The target " FORMAT_STR L" requires a value." #define CLI_PARSER_DETAILED_ERR_TARGET_VALUE_UNEXPECTED L"The target " FORMAT_STR L" does not accept a value." #define CLI_PARSER_DETAILED_ERR_PROPERTY_VALUE_REQUIRED L"The property " FORMAT_STR L" requires a value." #define CLI_PARSER_DETAILED_ERR_PROPERTY_VALUE_UNEXPECTED L"The property " FORMAT_STR L" does not accept a value." #define CLI_PARSER_DETAILED_ERR_OPTION_REQUIRED L"Missing required option " FORMAT_STR L"." #define CLI_PARSER_DETAILED_ERR_PROPERTY_REQUIRED L"Missing required property " FORMAT_STR L"." // Common CLI error messages defined in specification #define CLI_ERR_OPENING_CONFIG_PROTOCOL L"Error: Communication with the device driver failed." #define CLI_ERR_FAILED_TO_FIND_PROTOCOL L"Error: DCPMM_CONFIG2_PROTOCOL not found." #define CLI_ERR_INVALID_REGION_ID L"Error: The region identifier is not valid." #define CLI_ERR_INVALID_NAMESPACE_ID L"Error: The namespace identifier is not valid." #define CLI_ERR_NO_DIMMS_ON_SOCKET L"Error: There are no " PMEM_MODULES_STR L" on the specified socket(s)." #define CLI_ERR_NO_SPECIFIED_DIMMS_ON_SPECIFIED_SOCKET L"Error: None of the specified " PMEM_MODULE_STR L"(s) belong to the specified socket(s)." #define CLI_ERR_INVALID_SOCKET_ID L"Error: The socket identifier is not valid." #define CLI_ERR_OUT_OF_MEMORY L"Error: There is not enough memory to complete the requested operation." #define CLI_ERR_WRONG_FILE_PATH L"Error: Wrong file path." #define CLI_ERR_WRONG_FILE_DATA L"Error: Wrong data in the file." #define CLI_ERR_INTERNAL_ERROR L"Error: Internal function error." #define CLI_ERR_PROMPT_INVALID L"Error: Invalid data input." #define CLI_ERR_WRONG_DIAGNOSTIC_TARGETS L"Error: Invalid diagnostics target, valid values are: " FORMAT_STR #define CLI_ERR_WRONG_REGISTER L"Error: Register not found" #define CLI_ERR_INVALID_PASSPHRASE_FROM_FILE L"Error: The file contains empty or bad formatted passphrases." #define CLI_ERR_UNMANAGEABLE_DIMM L"Error: The specified device is not manageable by the driver." #define CLI_ERR_POPULATION_VIOLATION L"Error: The specified device is in population violation." #define CLI_ERR_REGION_TO_SOCKET_MAPPING L"The specified region id might not exist on the specified Socket(s).\n" #define CLI_ERR_PCD_CORRUPTED L"Error: Unable to complete operation due to existing PCD Configuration partition corruption. Use create -f -goal to override current PCD and create goal." #define CLI_ERR_OPENING_PBR_PROTOCOL L"Error: Communication with the device driver failed. Failed to obtain PBR protocol." #define CLI_WARNING_CLI_DRIVER_VERSION_MISMATCH L"Warning: There is a CLI and Driver version mismatch. Behavior is undefined." // Common CLI error messages defined in specification #define CLI_ERR_NO_COMMAND L"Syntax Error: No input." #define CLI_ERR_INCOMPLETE_SYNTAX L"Syntax Error: Incomplete syntax." #define CLI_ERR_UNSUPPORTED_COMMAND_SYNTAX L"Syntax Error: Invalid or unsupported command." #define CLI_ERR_INCORRECT_VALUE_POISON_TYPE L"Syntax Error: Incorrect value for property PoisonType." #define CLI_ERR_INCORRECT_VALUE_OPTION_DISPLAY L"Syntax Error: Incorrect value for option -d|-display." #define CLI_ERR_INCORRECT_VALUE_OPTION_UNITS L"Syntax Error: Incorrect value for option -units." #define CLI_ERR_INCORRECT_VALUE_OPTION_RECOVER L"Syntax Error: Incorrect value for option -recover." #define CLI_ERR_INCORRECT_VALUE_TARGET_REGISTER L"Syntax Error: Incorrect value for target -register." #define CLI_ERR_INCORRECT_VALUE_TARGET_DIMM L"Syntax Error: Incorrect value for target -dimm." #define CLI_ERR_INCORRECT_VALUE_TARGET_SOCKET L"Syntax Error: Incorrect value for target -socket." #define CLI_ERR_INCORRECT_VALUE_TARGET_NAMESPACE L"Syntax Error: Incorrect value for target -namespace." #define CLI_ERR_INCORRECT_VALUE_TARGET_REGION L"Syntax Error: Incorrect value for target -region." #define CLI_ERR_INCORRECT_VALUE_TARGET_ERROR L"Syntax Error: Incorrect value for target -error." #define CLI_ERR_INCORRECT_VALUE_TARGET_SMBIOS L"Syntax Error: Incorrect value for target -smbios." #define CLI_ERR_INCORRECT_VALUE_TARGET_SENSOR L"Syntax Error: Incorrect value for target -sensor." #define CLI_ERR_INCORRECT_VALUE_TARGET_PCD L"Syntax Error: Incorrect value for target -pcd." #define CLI_ERR_INCORRECT_VALUE_TARGET_PERFORMANCE L"Syntax Error: Incorrect value for target -performance." #define CLI_ERR_INCORRECT_VALUE_TARGET_EVENT L"Syntax Error: Incorrect value for target -event." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_MEMORY_MODE L"Syntax Error: Incorrect value for property MemoryMode." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_PERSISTENT_MEM_TYPE L"Syntax Error: Incorrect value for property PersistentMemoryType." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_RESERVED L"Syntax Error: Incorrect value for property Reserved." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_BLOCK_SIZE L"Syntax Error: Incorrect value for property BlockSize." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_BLOCK_COUNT L"Syntax Error: Incorrect value for property BlockCount." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_CAPACITY L"Syntax Error: Incorrect value for property Capacity." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_NAME L"Syntax Error: Incorrect value for property Name." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_ERASE_CAPABLE L"Syntax Error: Incorrect value for property EraseCapable." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_ENCRYPTION L"Syntax Error: Incorrect value for property Encryption." #define CLI_ERR_INCORRECT_PROPERTY_VALUE_MODE L"Syntax Error: Incorrect value for property Mode." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_RAW_CAPACITY L"Syntax Error: Incorrect value for property Size." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_ERASE_TYPE L"Syntax Error: Incorrect value for property EraseType." #define CLI_ERR_INCORRECT_VALUE_FOR_PROPERTY_AVG_PWR_REPORTING_TIME_CONSTANT L"Syntax Error: Incorrect value for property AveragePowerReportingTimeConstant." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_LEVEL L"Syntax Error: Incorrect value for property Level." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_COUNT L"Syntax Error: Incorrect value for property Count." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_CATEGORY L"Syntax Error: Incorrect value for property Category." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_SEQ_NUM L"Syntax Error: Incorrect value for property SequenceNumber." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_ALARM_THRESHOLD L"Syntax Error: Incorrect value for property AlarmThreshold." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_ENABLED_STATE L"Syntax Error: Incorrect value for property AlarmThreshold." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_NS_LABEL_VERSION L"Syntax Error: Incorrect value for property NamespaceLabelVersion." #define CLI_ERR_INCORRECT_VALUE_PROPERTY_CONFIG L"Syntax Error: Incorrect value for property Config." #define CLI_ERR_INCORRECT_VALUE_TARGET_TOKEN_ID L"Syntax Error: Incorrect value for target -tokens." #define CLI_ERROR_POISON_TYPE_WITHOUT_ADDRESS L"Syntax Error: Poison type property should be followed by poison address." #define CLI_ERROR_CLEAR_PROPERTY_NOT_COMBINED L"Syntax Error: Clear property should be given in combination with other error injection properties." #define CLI_ERR_MISSING_VALUE_PROPERTY_TYPE L"Syntax Error: Type property not provided." #define CLI_ERR_INCORRECT_VALUE_FOR_PROPERTY L" is not a valid setting for the property." #define CLI_SYNTAX_ERROR L" Syntax Error: " #define CLI_ERR_OPTIONS_ALL_DISPLAY_USED_TOGETHER L"Syntax Error: Options -a|-all and -d|-display can not be used together." #define CLI_ERR_OPTIONS_EXAMINE_USED_TOGETHER L"Syntax Error: Options -x and -examine can not be used together." #define CLI_ERR_OPTIONS_FORCE_USED_TOGETHER L"Syntax Error: Options -f and -force can not be used together." #define CLI_ERR_VALUES_APPDIRECT_SIZE_USED_TOGETHER L"Syntax Error: Option values AppDirectSize and AppDirect1Size can not be used together." #define CLI_ERR_VALUES_APPDIRECT_INDICES_USED_TOGETHER L"Syntax Error: Option values AppDirectIndex and AppDirect1Index can not be used together." #define CLI_ERR_PROPERTIES_CAPACITY_BLOCKCOUNT_USED_TOGETHER L"Syntax Error: Properties Capacity and BlockCount can not be used together." #define CLI_ERR_PROPERTIES_MEMORYMODE_RESERVED_TOO_LARGE L"Syntax Error: Properties MemoryMode and Reserved cannot sum greater than 100%%%%" //%%%% because format string is processed twice #define CLI_INFO_NO_DIMMS L"No " PMEM_MODULES_STR L" in the system." #define CLI_INFO_NO_FUNCTIONAL_DIMMS L"No functional " PMEM_MODULES_STR L" in the system." #define CLI_INFO_NO_REGIONS L"There are no Regions defined in the system." #define CLI_INFO_NO_MANAGEABLE_DIMMS L"No manageable " PMEM_MODULES_STR L" in the system." #define CLI_INFO_NO_NON_FUNCTIONAL_DIMMS L"No non-functional " PMEM_MODULES_STR L" in the system." #define CLI_INFO_SHOW_REGION L"Show Region" #define CLI_INFO_NO_NAMESPACES_DEFINED L"No Namespaces defined in the system." #define CLI_INFO_SHOW_NAMESPACE L"Show Namespace" #define CLI_INFO_SET_NAMESPACE L"Set Namespace" #define CLI_INFO_DELETE_NAMESPACE L"Delete Namespace" #define CLI_INFO_DUMP_DEBUG_LOG L"Dump Debug Log" #define CLI_INFO_LOAD_GOAL L"Load Goal" #define CLI_INFO_LOAD_GOAL_CONFIRM_PROMPT L"Load the configuration goal from '" FORMAT_STR L"' which will delete existing data and provision the capacity of the " PMEM_MODULES_STR L" on the next reboot." #define CLI_INFO_SHOW_REGISTER L"Show Register" #define CLI_INFO_SHOW_PCD L"Show Platform Config Data" #define CLI_ERR_MASTER_PASSPHRASE_NOT_ENABLED L"Master Passphrase not enabled on specified " PMEM_MODULES_STR L"." #define CLI_ERR_MISSING_PASSPHRASE_PROPERTY L"Syntax Error: Passphrase property not provided." #define CLI_ERR_DEFAULT_OPTION_NOT_COMBINED L"Syntax Error: Default option should be given in combination with master option." #define CLI_ERR_DEFAULT_OPTION_PASSPHRASE_PROPERTY_USED_TOGETHER L"Syntax Error: Passphrase property and default option cannot be used together." #define CLI_ERR_DEFAULT_NOT_SUPPORTED_FIRMWARE_REV L"Default option for Master Passphrase is only supported for setting Master Passphrase on at least one of the " PMEM_MODULES_STR "." #define CLI_ERR_FORCE_REQUIRED L"Error: This command requires force option." #define CLI_ERR_INVALID_BLOCKSIZE_FOR_CAPACITY L"Error: Capacity property can only be used with 512 or 4096 bytes block size." #define CLI_ERR_INVALID_NAMESPACE_CAPACITY L"Error: Invalid value for namespace capacity." #define CLI_ERR_SOME_VALUES_NOT_SUPPORTED L"Error: One or more of the fields specified are not supported on all the " PMEM_MODULES_STR L"." #define CLI_ERR_PRINTING_DIAGNOSTICS_RESULTS L"Error: Printing of diagnostics results failed." #define CLI_INJECT_ERROR_FAILED L"Error: Inject error command failed" #define CLI_ERR_NOT_UTF16 L"Error: The file is not in UTF16 format, BOM header missing.\n" #define CLI_ERR_EMPTY_FILE L"Error: The file is empty.\n" #define CLI_ERR_NO_SOCKET_SKU_SUPPORT L"Platform does not support socket SKU limits.\n" #define CLI_ERR_SOCKET_NOT_FOUND L"Socket not found. Invalid SocketID: %d\n" #define CLI_ERR_CAPACITY_STRING L"Error: Failed creating the capacity string." #define CLI_INFO_LOAD_FW L"Load FW" #define CLI_INFO_LOAD_RECOVER_FW L"Load recovery FW" #define CLI_INFO_LOAD_RECOVER_INVALID_DIMM L"The specified " PMEM_MODULE_STR L" does not exist or is not in a non-functional state." #define CLI_INFO_ON L" on" #define CLI_PROGRESS_STR L"\rOperation on " PMEM_MODULE_STR L" 0x%04x Progress: %d%%" #define CLI_LOAD_MFG_FW L"MFG Load Prod FW" #define CLI_INJECT_MFG L"MFG Inject command" #define CLI_MEM_INFO_MFG L"MFG Mem Info page" #define CLI_INFO_SET_FW_LOG_LEVEL L"Set firmware log level" #define CLI_INFO_PACKAGE_SPARING_INJECT_ERROR L"Trigger package sparing" #define CLI_INFO_POISON_INJECT_ERROR L"Poison address " FORMAT_STR #define CLI_INFO_PERCENTAGE_REMAINING_INJECT_ERROR L"Trigger a percentage remaining" #define CLI_INFO_FATAL_MEDIA_ERROR_INJECT_ERROR L"Create a media fatal error" #define CLI_INFO_DIRTY_SHUT_DOWN_INJECT_ERROR L"Trigger a dirty shut down" #define CLI_INFO_TEMPERATURE_INJECT_ERROR L"Set temperature" #define CLI_INFO_CLEAR_PACKAGE_SPARING_INJECT_ERROR L"Clear injected package sparing" #define CLI_INFO_CLEAR_POISON_INJECT_ERROR L"Clear injected poison of address " FORMAT_STR #define CLI_INFO_CLEAR_PERCENTAGE_REMAINING_INJECT_ERROR L"Clear injected percentage remaining" #define CLI_INFO_CLEAR_FATAL_MEDIA_ERROR_INJECT_ERROR L"Clear injected media fatal error" #define CLI_INFO_CLEAR_DIRTY_SHUT_DOWN_INJECT_ERROR L"Clear dirty shut down" #define CLI_INFO_CLEAR_TEMPERATURE_INJECT_ERROR L"Clear injected temperature" #define PROMPT_CONTINUE_QUESTION L"Do you want to continue? [y/n] " #define CLI_CREATE_GOAL_PROMPT_VOLATILE L"The requested goal was adjusted more than 10%% to find a valid configuration." #define CLI_CREATE_GOAL_PROMPT_HEADER L"The following configuration will be applied:" #define CLI_WARN_GOAL_CREATION_SECURITY_UNLOCKED L"WARNING: Goal will not be applied unless security is disabled prior to platform firmware (BIOS) provisioning!" #define CLI_ERR_CREATE_GOAL_AUTO_PROV_ENABLED L"Error: Automatic provisioning is enabled. Please disable to manually create goals." #define CLI_CREATE_NAMESPACE_PROMPT_ROUNDING_CAPACITY L"The requested namespace capacity %lld B will be rounded up to %lld B to align properly." #define REGION_FOUND L"REGION FOUND." #define CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE L"Unable to retrieve user display preferences." #define CLI_DOWNGRADE_PROMPT L"Downgrade firmware on " PMEM_MODULE_STR L" " FORMAT_STR L"?" #define CLI_RECOVER_DIMM_PROMPT_STR L"Recover " PMEM_MODULE_STR L":" #define CLI_FORMAT_DIMM_REBOOT_REQUIRED_STR L"A power cycle is required after a device format." #define CLI_FORMAT_DIMM_PROMPT_STR L"This operation will take several minutes to complete and will erase all data on " PMEM_MODULE_STR L" " #define CLI_INFO_START_FORMAT L"Format" #define CLI_FORMAT_DIMM_STARTING_FORMAT L"Formatting " PMEM_MODULE_STR L"(s)..." #define CLI_INFO_DUMP_SUPPORT_SUCCESS L"Dump support data successfully written to " FORMAT_STR L"." #define CLI_INFO_DUMP_CONFIG_SUCCESS L"Successfully dumped system configuration to file: " FORMAT_STR_NL #define CLI_ERR_INJECT_FATAL_ERROR_UNSUPPORTED_ON_OS L"Injecting a Fatal Media error is unsupported on this OS.\nPlease contact your OSV for assistance in performing this action." #define CLI_ERR_TRANSPORT_PROTOCOL_UNSUPPORTED_ON_OS L"The following protocol " FORMAT_STR L" is unsupported on this OS. Please use " FORMAT_STR L" instead.\n" #define PRINT_SETTINGS_FORMAT_FOR_SHOW_SYS_CAP_CMD 1 #define PRINT_SETTINGS_FORMAT_FOR_SHOW_REGION_CMD 2 #define CLI_ERR_FAILED_TO_GET_PBR_MODE L"Failed to get the current PBR mode." #define CLI_ERR_FAILED_TO_SET_PBR_MODE L"Failed to configure the playback and record mode to: " FORMAT_STR #define CLI_ERR_FAILED_NO_PBR_SESSION_LOADED L"Failed to configure the playback mode. No PBR session loaded." #define CLI_ERR_FAILED_TO_SET_SESSION_TAG L"Failed to set the current tag location." #define CLI_ERR_FAILED_TO_GET_SESSION_TAG L"Failed to get the current tag location." #define CLI_ERR_FAILED_TO_GET_SESSION_TAG_COUNT L"Failed to get the session tag count." #define CLI_ERR_FAILED_DURING_DRIVER_INIT L"Driver binding start failed." #define CLI_ERR_FAILED_DURING_DRIVER_UNINIT L"Driver binding stop failed." #define CLI_ERR_FAILED_DURING_CMD_EXECUTION L"Executing CMD during playback failed." #define CLI_ERR_UNKNOWN_MODE L"Unknown PBR mode." #define CLI_ERR_FAILED_TO_GET_SESSION_BUFFER L"Failed to get the current session buffer." #define CLI_ERR_FAILED_TO_DUMP_SESSION_TO_FILE L"Failed to dump contents of session buffer to a file." #define CLI_ERR_FAILED_TO_GET_FILE_PATH L"Failed to get file path " FORMAT_EFI_STATUS #define CLI_ERR_FAILED_TO_READ_FILE L"Failed to read pbr file." #define CLI_ERR_FAILED_TO_SET_SESSION_BUFFER L"Failed to set session buffer." #define CLI_ERR_FAILED_TO_RESET_SESSION L"Failed to reset the session." #define CLI_ERR_CMD_FAILED_NOT_ADMIN L"Error: The ipmctl command you have attempted to execute requires administrator privileges." #define ERROR_CHECKING_MIXED_SKU L"Error: Could not check if SKU is mixed." #define WARNING_DIMMS_SKU_MIXED L"Warning: Mixed SKU detected. Driver functionalities limited.\n" /** This prevents use of strlen on NULL string. StrLen fn in MdePkg has an assert if it is null. **/ #define StrLen(Str) (((void *)(Str)==NULL)?(0):(StrLen(Str))) /** sizeof returns the number of bytes that the array uses. We need to divide it by the length of a single pointer to get the number of elements. **/ #define ALLOWED_DISP_VALUES_COUNT(A) (sizeof(A)/sizeof(CHAR16*)) /** GUID for NvmDimmCli Variables for Get/Set via runtime services. **/ #define NVMDIMM_CLI_NGNVM_VARIABLE_GUID \ { 0x11c64219, 0xbfa2, 0x42ce, {0x99, 0xb1, 0x17, 0x0b, 0x4a, 0x2b, 0xe0, 0x8e}} /** Retrieve a populated array and count of DIMMs in the system. The caller is responsible for freeing the returned array @param[in] pNvmDimmConfigProtocol A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pCmd A pointer to a COMMAND struct. Used to obtain the Printer context. printed to stdout, otherwise will be directed to the printer module. @param[in] dimmInfoCategories Categories that will be populated in the DIMM_INFO struct. @param[out] ppDimms A pointer to the dimm list found in NFIT. @param[out] pDimmCount A pointer to the number of DIMMs found in NFIT. @retval EFI_SUCCESS the dimm list was returned properly @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_NOT_FOUND dimm not found **/ EFI_STATUS GetDimmList( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN struct Command *pCmd, IN DIMM_INFO_CATEGORIES dimmInfoCategories, OUT DIMM_INFO **ppDimms, OUT UINT32 *pDimmCount ); /** Retrieve a populated array and count of all DCPMMs (functional and non-functional) in the system. The caller is responsible for freeing the returned array @param[in] pNvmDimmConfigProtocol A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pCmd A pointer to a COMMAND struct. Used to obtain the Printer context. printed to stdout, otherwise will be directed to the printer module. @param[in] dimmInfoCategories Categories that will be populated in the DIMM_INFO struct. @param[out] ppDimms A pointer to a combined DCPMM list (initialized and uninitialized) from NFIT. @param[out] pDimmCount A pointer to the total number of DCPMMs found in NFIT. @retval EFI_SUCCESS the dimm list was returned properly @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_NOT_FOUND dimm not found **/ EFI_STATUS GetAllDimmList( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN struct Command *pCmd, IN DIMM_INFO_CATEGORIES dimmInfoCategories, OUT DIMM_INFO **ppDimms, OUT UINT32 *pDimmCount ); /** Parse the string and return the array of unsigned integers Example String: "1,3,7" Array[0]: 1 Array[1]: 3 Array[2]: 7 @param[in] pString string to parse @param[out] ppUints allocated, filled array with the uints @param[out] pUintsNum size of uints array @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER inputs are null, the format of string is not proper, duplicated Dimm IDs @retval EFI_NOT_FOUND Dimm not found **/ EFI_STATUS GetUintsFromString( IN CHAR16 *pString, OUT UINT16 **ppUints, OUT UINT32 *pUintsNum ); /** Parses the dimm target string (which can contain DimmIDs as SMBIOS type-17 handles and/or DimmUIDs), and returns an array of DimmIDs in the SMBIOS physical-id forms. Also checks for invalid DimmIDs and duplicate entries. Example String: "8089-00-0000-13325476,30,0x0022" Array[0]: 28 Array[1]: 30 Array[2]: 34 @param[in] pCmd A pointer to a COMMAND struct. Used to obtain the Printer context. @param[in] pDimmString The dimm target string to parse. @param[in] pDimmInfo The dimm list found in NFIT. @param[in] DimmCount Size of the pDimmInfo array. @param[out] ppDimmIds Pointer to the array allocated and filled with the SMBIOS DimmIDs. @param[out] pDimmIdsCount Size of the pDimmIds array. @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER the format of string is not proper @retval EFI_NOT_FOUND dimm not found **/ EFI_STATUS GetDimmIdsFromString( IN struct Command *pCmd, IN CHAR16 *pDimmString, IN DIMM_INFO *pDimmInfo, IN UINT32 DimmCount, OUT UINT16 **ppDimmIds, OUT UINT32 *pDimmIdsCount ); /** Parses the dimm target string (which can contain DimmIDs as SMBIOS type-17 handles and/or DimmUIDs), and returns a DimmUid. Example String: "8089-00-0000-13325476" or "30" or "0x0022" @param[in] pDimmString The dimm target string to parse. @param[in] pDimmInfo The dimm list found in NFIT. @param[in] DimmCount Size of the pDimmInfo array. @param[out] pDimmUid Pointer to the NVM_UID buffer. @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER the format of string is not proper @retval EFI_NOT_FOUND dimm not found **/ EFI_STATUS GetDimmUidFromString( IN CHAR16 *pDimmString, IN DIMM_INFO *pDimmInfo, IN UINT32 DimmCount, OUT CHAR8 *pDimmUid ); /** Check if the uint is in the uints array @param[in] pUints - array of the uints @param[in] UintsNum number of uints in the array @param[in] UintToFind searched uint @retval TRUE if the uint has been found @retval FALSE if the uint has not been found **/ BOOLEAN ContainUint( IN UINT16 *pSockets, IN UINT32 SocketNum, IN UINT16 Socket ); /** Check if the Guid is in the Guids array @param[in] ppGuids array of the Guid pointers @param[in] GuidsNum number of Guids in the array @param[in] pGuidToFind pointer to GUID with information to find @retval TRUE if table contains guid with same data as *pGuidToFind @retval FALSE **/ BOOLEAN ContainGuid( IN GUID **ppGuids, IN UINT32 GuidsNum, IN GUID *pGuidToFind ); /** Checks if the provided display list string contains only the valid values. @param[in] pDisplayValues pointer to the Unicode string containing the user input display list. @param[in] ppAllowedDisplayValues pointer to an array of Unicode strings that define the valid display values. @param[in] Count is the number of valid display values in ppAllowedDisplayValues. @retval EFI_SUCCESS if all of the provided display values are valid. @retval EFI_OUT_OF_RESOURCES if the memory allocation fails. @retval EFI_INVALID_PARAMETER if one or more of the provided display values is not a valid one. Or if pDisplayValues or ppAllowedDisplayValues is NULL. **/ EFI_STATUS CheckDisplayList( IN CHAR16 *pDisplayValues, IN CHAR16 **ppAllowedDisplayValues, IN UINT16 Count ); /** Gets number of Manageable and supported Dimms and their IDs and Handles @param[in] pNvmDimmConfigProtocol A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] CheckSupportedConfigDimm If true, include dimms in unmapped set of dimms (non-POR) in returned dimm list. If false, skip these dimms from returned list. @param[out] DimmIdsCount is the pointer to variable, where number of dimms will be stored. @param[out] ppDimmIds is the pointer to variable, where IDs of dimms will be stored. @retval EFI_NOT_FOUND if the connection with NvmDimmProtocol can't be established @retval EFI_OUT_OF_RESOURCES if the memory allocation fails. @retval EFI_INVALID_PARAMETER if number of dimms or dimm IDs have not been assigned properly. @retval EFI_SUCCESS if successfully assigned number of dimms and IDs to variables. **/ EFI_STATUS GetManageableDimmsNumberAndId( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN BOOLEAN CheckSupportedConfigDimm, OUT UINT32 *pDimmIdsCount, OUT UINT16 **ppDimmIds ); /** Gets number of Manageable (functional and non-functional) and supported Dimms and their IDs and Handles @param[in] pNvmDimmConfigProtocol A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] CheckSupportedConfigDimm If true, include dimms in unmapped set of dimms (non-POR) in returned dimm list. If false, skip these dimms from returned list. @param[out] DimmIdsCount is the pointer to variable, where number of dimms will be stored. @param[out] ppDimmIds is the pointer to variable, where IDs of dimms will be stored. @retval EFI_NOT_FOUND if the connection with NvmDimmProtocol can't be established @retval EFI_OUT_OF_RESOURCES if the memory allocation fails. @retval EFI_INVALID_PARAMETER if number of dimms or dimm IDs have not been assigned properly. @retval EFI_SUCCESS if successfully assigned number of dimms and IDs to variables. **/ EFI_STATUS GetAllManageableDimmsNumberAndId( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN BOOLEAN CheckSupportedConfigDimm, OUT UINT32 *pDimmIdsCount, OUT UINT16 **ppDimmIds ); /** Checks if user has specified the options -a|-all and -d|-display. Those two flags exclude each other so the function also checks if the user didn't provide them both. If the -d|-display option has been found, the its values are checked against the allowed values for this parameter. @param[in] pCommand is the pointer to a Command structure that is tested for the options presence. @param[in] ppAllowedDisplayValues is a pointer to an array of Unicode strings considered as the valid values for the -d|-display option. @param[in] AllowedDisplayValuesCount is a UINT32 value that represents the number of elements in the array pointed by ppAllowedDisplayValues. @param[out] pDispOptions contains the following. A BOOLEAN value that will represent the presence of the -a|-all option in the Command pointed by pCommand. A BOOLEAN value that will represent the presence of the -d|-display option in the Command pointed by pCommand. A pointer to an Unicode string. If the -d|-display option is present, this pointer will be set to the option value Unicode string. @retval EFI_SUCCESS the check went fine, there were no errors @retval EFI_INVALID_PARAMETER if the user provided both options, the display option has been provided and has some invalid values or if at least one of the input pointer parameters is NULL. @retval EFI_OUT_OF_RESOURCES if the memory allocation fails. **/ EFI_STATUS CheckAllAndDisplayOptions( IN struct Command *pCommand, IN CHAR16 **ppAllowedDisplayValues, IN UINT32 AllowedDisplayValuesCount, OUT CMD_DISPLAY_OPTIONS *pDispOptions ); /** Retrieve property by name and assign its value to UINT64. @param[in] pCmd Command containing the property @param[in] pPropertyName String with property name @param[out] pOutValue target UINT64 value @retval FALSE if there was no such property or it doesn't contain a valid value **/ BOOLEAN PropertyToUint64 ( IN struct Command *pCmd, IN CHAR16 *pPropertyName, OUT UINT64 *pOutValue ); /** Retrieve property by name and assign its value to double @param[in] pCmd Command containing the property @param[in] pPropertyName String with property name @param[out] pOutValue Target double value @retval EFI_INVALID_PARAMETER Property not found or no valid value inside @retval EFI_SUCCESS Conversion successful **/ EFI_STATUS PropertyToDouble ( IN struct Command *pCmd, IN CHAR16 *pPropertyName, OUT double *pOutValue ); /** Extracts working directory path from file path @param[in] pUserFilePath Pointer to string with user specified file path @param[out] pOutFilePath Pointer to actual file path @param[out] ppDevicePath Pointer to where to store device path @retval EFI_SUCCESS Extraction success @retval EFI_INVALID_PARAMETER Invalid parameter @retval EFI_OUT_OF_RESOURCES Out of resources **/ EFI_STATUS GetDeviceAndFilePath( IN CHAR16 *pUserFilePath, IN OUT CHAR16 *pOutFilePath, IN OUT EFI_DEVICE_PATH_PROTOCOL **ppDevicePath ); /** Match driver command status to CLI return code @param[in] Status - NVM_STATUS returned from driver @retval - Appropriate EFI return code **/ EFI_STATUS MatchCliReturnCode ( IN NVM_STATUS Status ); /** Get free space of volume from given path @param[in] pFileHandle - file handle protocol @param[out] pFreeSpace - free space @retval - Appropriate EFI return code **/ EFI_STATUS GetVolumeFreeSpace( IN EFI_FILE_HANDLE pFileHandle, OUT UINT64 *pFreeSpace ); /** Check if file exists @param[in] pDumpUserPath - destination file path @param[out] pExists - pointer to whether or not destination file already exists @retval - Appropriate EFI return code **/ EFI_STATUS FileExists ( IN CHAR16* pDumpUserPath, OUT BOOLEAN* pExists ); /** Delete file @param[in] pDumpUserPath - file path to delete @retval - Appropriate EFI return code **/ EFI_STATUS DeleteFile ( IN CHAR16* pDumpUserPath ); /** Dump data to file @param[in] pDumpUserPath - destination file path @param[in] BufferSize - data size to write @param[in] pBuffer - pointer to buffer @param[in] Overwrite - enforce overwriting file @retval - Appropriate EFI return code **/ EFI_STATUS DumpToFile ( IN CHAR16* pDumpUserPath, IN UINT64 BufferSize, IN VOID* pBuffer, IN BOOLEAN Overwrite ); /** Prints supported or recommended appdirect settings @param[in] pInterleaveFormatList pointer to variable length interleave formats array @param[in] FormatNum number of the appdirect settings formats @param[in] pInterleaveSize pointer to Channel & iMc interleave size @param[in] PrintRecommended if TRUE Recommended settings will be printed if FALSE Supported settings will be printed @param[in] Mode Set mode to print different format @retval String representing AppDirect settings. Null on error. **/ CHAR16* PrintAppDirectSettings( IN VOID *pInterleaveFormatList, IN UINT16 FormatNum, IN INTERLEAVE_SIZE *pInterleaveSize, IN BOOLEAN PrintRecommended, IN UINT8 Mode ); /** Read source file and return current passphrase to unlock device. @param[in] pCmd A pointer to a COMMAND struct. Used to obtain the Printer context. @param[in] pFileHandle File handler to read Passphrase from @param[in] pDevicePath - handle to obtain generic path/location information concerning the physical device or logical device. The device path describes the location of the device the handle is for. @param[out] Current passphrase @retval EFI_SUCCESS File load and parse success @retval EFI_INVALID_PARAMETER Invalid Parameter during load @retval other Return Codes from TrimLineBuffer, GetLoadPoolData, GetLoadDimmData, GetLoadValue functions **/ EFI_STATUS ParseSourcePassFile( IN struct Command *pCmd, IN CHAR16 *pFilePath, IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath, OUT CHAR16 **ppCurrentPassphrase OPTIONAL, OUT CHAR16 **ppNewPassphrase OPTIONAL ); /** Prompted input request @param[in] pPrompt - information about expected input @param[in] ShowInput - Show characters written by user @param[in] OnlyAlphanumeric - Allow only for alphanumeric characters @param[out] ppReturnValue - is a pointer to a pointer to the 16-bit character string that will contain the return value @retval - Appropriate CLI return code **/ EFI_STATUS PromptedInput( IN CHAR16 *pPrompt, IN BOOLEAN ShowInput, IN BOOLEAN OnlyAlphanumeric, OUT CHAR16 **ppReturnValue ); /** Display "yes/no" question and retrieve reply using prompt mechanism @param[out] pConfirmation Confirmation from prompt @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All Ok **/ EFI_STATUS PromptYesNo( OUT BOOLEAN *pConfirmation ); /** Read input from console @param[in] ShowInput - Show characters written by user @param[in] OnlyAlphanumeric - Allow only for alphanumeric characters @param[in, out] ppReturnValue - is a pointer to a pointer to the 16-bit character string without null-terminator that will contain the return value @param[in, out] pBufferSize - is a pointer to the Size in bytes of the return buffer @retval - Appropriate CLI return code **/ EFI_STATUS ConsoleInput( IN BOOLEAN ShowInput, IN BOOLEAN OnlyAlphanumeric, IN OUT CHAR16 **ppReturnValue, IN OUT UINTN *pBufferSize OPTIONAL ); /** Print Load Firmware progress for all DIMMs @param[in] ProgressEvent EFI Event @param[in] pContext context pointer **/ VOID EFIAPI PrintProgress( IN EFI_EVENT ProgressEvent, IN VOID *pContext ); /** Get relative path from absolute path @param[in] pAbsolutePath Absolute path @param[out] ppRelativePath Relative path @retval EFI_INVALID_PARAMETER Input parameter was NULL @retval EFI_SUCCESS All Ok **/ EFI_STATUS GetRelativePath( IN CHAR16 *pAbsolutePath, OUT CHAR16 **ppRelativePath ); /** Check if all dimms in the specified pDimmIds list are manageable. This helper method assumes all the dimms in the list exist. This helper method also assumes the parameters are non-null. @param[in] pAllDimms The dimm list found in NFIT @param[in] AllDimmCount Size of the pAllDimms array @param[in] pDimmsListToCheck Pointer to the array of DimmIDs to check @param[in] DimmsToCheckCount Size of the pDimmsListToCheck array @retval TRUE if all Dimms in pDimmsListToCheck array are manageable @retval FALSE if at least one DIMM is not manageable **/ BOOLEAN AllDimmsInListAreManageable( IN DIMM_INFO *pAllDimms, IN UINT32 AllDimmCount, IN UINT16 *pDimmsListToCheck, IN UINT32 DimmsToCheckCount ); /** Check if all dimms in the specified pDimmIds list are in supported config. This helper method assumes all the dimms in the list exist. This helper method also assumes the parameters are non-null. @param[in] pAllDimms The dimm list found in NFIT @param[in] AllDimmCount Size of the pAllDimms array @param[in] pDimmsListToCheck Pointer to the array of DimmIDs to check @param[in] DimmsToCheckCount Size of the pDimmsListToCheck array @retval TRUE if all Dimms in pDimmsListToCheck array are in supported config @retval FALSE if at least one DIMM is not in supported config **/ BOOLEAN AllDimmsInListInSupportedConfig( IN DIMM_INFO *pAllDimms, IN UINT32 AllDimmCount, IN UINT16 *pDimmsListToCheck, IN UINT32 DimmsToCheckCount ); /** Check if all dimms in the specified pDimmIds list have master passphrase enabled. This helper method assumes all the dimms in the list exist. This helper method also assumes the parameters are non-null. @param[in] pAllDimms The dimm list found in NFIT @param[in] AllDimmCount Size of the pAllDimms array @param[in] pDimmsListToCheck Pointer to the array of DimmIDs to check @param[in] DimmsToCheckCount Size of the pDimmsListToCheck array @param[in] SkipFIS32Dimms indicates that Dimms with FIS 3.2 or above should always pass @retval TRUE if all Dimms in pDimmsListToCheck array have master passphrase enabled @retval FALSE if at least one DIMM does not have master passphrase enabled **/ BOOLEAN AllDimmsInListHaveMasterPassphraseEnabled( IN DIMM_INFO *pAllDimms, IN UINT32 AllDimmCount, IN UINT16 *pDimmsListToCheck, IN UINT32 DimmsToCheckCount, IN BOOLEAN SkipFIS32Dimms ); /** Get Dimm identifier preference @param[out] pDimmIdentifier Variable to store Dimm identifier preference @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS GetDimmIdentifierPreference( OUT UINT8 *pDimmIdentifier ); /** Get Dimm identifier as string based on user preference @param[in] DimmId Dimm ID as number @param[in] pDimmUid Dimm UID as string @param[out] pResultString String representation of preferred value @param[in] ResultStringLen Length of pResultString @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS GetPreferredDimmIdAsString( IN UINT32 DimmId, IN CHAR16 *pDimmUid OPTIONAL, OUT CHAR16 *pResultString, IN UINT32 ResultStringLen ); /** Retrieve Display DimmID Runtime Index from Property String @param[in] String to try to discover index for @retval DimmID Index of DimmID property string @retval Size of Array if not found **/ UINT8 GetDimmIDIndex( IN CHAR16 *pDimmIDStr ); /** Retrieve Display Size Runtime Index from Property String @param[in] String to try to discover index for @retval Display Size Index of Size property string @retval Size of Array if not found **/ UINT8 GetDisplaySizeIndex( IN CHAR16 *pSizeStr ); /** Retrieve Display DimmID String from RunTime variable index @param[in] Index to retrieve @retval NULL Index was invalid @retval DimmID String of user display preference **/ CONST CHAR16 *GetDimmIDStr( IN UINT8 DimmIDIndex ); /** Retrieve Display Size String from RunTime variable index @param[in] Index to retrieve @retval NULL Index was invalid @retval Size String of user display preference **/ CONST CHAR16 *GetDisplaySizeStr( IN UINT16 DisplaySizeIndex ); /** Allocate and return string which is related with the binary RegionType value. The caller function is obligated to free memory of the returned string. @param[in] RegionType - region type @retval - output string **/ CHAR16 * RegionTypeToString( IN UINT8 RegionType ); /** Gets the DIMM handle corresponding to Dimm PID and also the index @param[in] DimmId - DIMM ID @param[in] pDimms - List of DIMMs @param[in] DimmsNum - Number of DIMMs @param[out] pDimmHandle - The Dimm Handle corresponding to the DIMM ID @param[out] pDimmIndex - The Index of the found DIMM @retval - EFI_STATUS Success @retval - EFI_INVALID_PARAMETER Invalid parameter @retval - EFI_NOT_FOUND Dimm not found **/ EFI_STATUS GetDimmHandleByPid( IN UINT16 DimmId, IN DIMM_INFO *pDimms, IN UINT32 DimmsNum, OUT UINT32 *pDimmHandle, OUT UINT32 *pDimmIndex ); /** Retrieve the User Cli Display Preferences CMD line arguments. @param[out] pDisplayPreferences pointer to the current driver preferences. @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS ReadCmdLinePrintOptions( IN OUT PRINT_FORMAT_TYPE *pFormatType, IN struct Command *pCmd ); /** Helper to recreate -o args in string format @param[in] pCmd command from CLI @param[out] ppOutputStr resulting -o string @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd or ppOutputStr is NULL **/ EFI_STATUS CreateCmdLineOutputStr( IN struct Command *pCmd, OUT CHAR16 **ppOutputStr ); /** Convert UEFI return codes to legacy OS return codes @param[in] UefiReturnCode - return code to Convert @retval - Converted OS ReturnCode **/ EFI_STATUS UefiToOsReturnCode(EFI_STATUS UefiReturnCode); /** Checks if user has incorrectly used master and default options. Also checks for invalid combinations of these options with the Passphrase property. @param[in] pCmd command from CLI @param[in] isPassphraseProvided TRUE if user provided passphrase @param[in] isMasterOptionSpecified TRUE if master option is specified @param[in] isDefaultOptionSpecified TRUE if default option is specified @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS CheckMasterAndDefaultOptions( IN struct Command *pCmd, IN BOOLEAN isPassphraseProvided, IN BOOLEAN isMasterOptionSpecified, IN BOOLEAN isDefaultOptionSpecified ); /** Retrieves a list of Dimms that have at least one NS. @param[in,out] pDimmIds the dimm IDs which have NS @param[in,out] pDimmIdCount count of dimm IDs @param[in] maxElements the maximum size of the dimm ID list @retval EFI_ABORTED Operation Aborted @retval EFI_OUT_OF_RESOURCES unable to allocate memory @retval EFI_SUCCESS All Ok **/ EFI_STATUS GetDimmIdsWithNamespaces( IN OUT UINT16 *pDimmIds, IN OUT UINT32 *pDimmIdCount, IN UINT32 maxElements); /** Adds an element to a element list without allowing duplication @param[in,out] pElementList the list @param[in,out] pElementCount size of the list @param[in] newElement the new element to add @param[in] maxElements the maximum size of the list @retval EFI_OUT_OF_RESOURCES unable to add any more elements @retval EFI_SUCCESS All Ok **/ EFI_STATUS AddElement( IN OUT UINT16 *pElementList, IN OUT UINT32 *pElementCount, IN UINT16 newElement, IN UINT32 maxElements); /** Checks whether the FW on the dimm restricts executing commands with the default Master Passphrase @param[in] DimmInfo is the information about the dimm to check @retval whether default is restricted by the FW's API version **/ BOOLEAN IsDefaultMasterPassphraseRestricted( IN DIMM_INFO DimmInfo); #endif /** _COMMON_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/CreateGoalCommand.c000066400000000000000000000620701440615110200213650ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "Debug.h" #include "Types.h" #include "Utility.h" #include "Common.h" #include "NvmInterface.h" #include "CommandParser.h" #include "CreateGoalCommand.h" #include "NvmDimmCli.h" #include "ShowGoalCommand.h" #include #include #define CREATE_GOAL_COMMAND_STATUS_HEADER L"Create region configuration goal" #define CREATE_GOAL_COMMAND_STATUS_CONJUNCTION L" on" #define IS_DIMM_UNLOCKED(SecurityStateBitmask) ((SecurityStateBitmask & SECURITY_MASK_ENABLED) && !(SecurityStateBitmask & SECURITY_MASK_LOCKED)) /** Command syntax definition **/ struct Command CreateGoalCommand = { CREATE_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"", HELP_VERBOSE_DETAILS_TEXT,FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {FORCE_OPTION_SHORT, FORCE_OPTION, L"", L"",HELP_FORCE_DETAILS_TEXT, FALSE, ValueEmpty}, {UNITS_OPTION_SHORT, UNITS_OPTION, L"", UNITS_OPTION_HELP,HELP_UNIT_DETAILS_TEXT, FALSE, ValueRequired}, #ifdef OS_BUILD {OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired} #endif }, { //!< targets {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional}, {GOAL_TARGET, L"", L"", TRUE, ValueEmpty}, {SOCKET_TARGET, L"", HELP_TEXT_SOCKET_IDS, FALSE, ValueOptional} }, { //!< properties {MEMORY_MODE_PROPERTY, L"", HELP_TEXT_PERCENT, FALSE, ValueRequired}, {PERSISTENT_MEM_TYPE_PROPERTY, L"", HELP_TEXT_PERSISTENT_MEM_TYPE, FALSE, ValueRequired}, {RESERVED_PROPERTY, L"", HELP_TEXT_PERCENT, FALSE, ValueRequired}, {NS_LABEL_VERSION_PROPERTY, L"", HELP_TEXT_NS_LABEL_VERSION, FALSE, ValueRequired} }, L"Provision capacity on one or more " PMEM_MODULES_STR L" into regions.", //!< help CreateGoal, TRUE, //!< enable print control support }; /** Traverse targeted dimms to determine if Security is enabled in Unlocked state @param[in] SecurityFlag Security mask from FW @retval TRUE if at least one targeted dimm has security enabled in unlocked state @retval FALSE if none of targeted dimms have security enabled in unlocked state **/ EFI_STATUS EFIAPI AreRequestedDimmsSecurityUnlocked( IN DIMM_INFO *pDimmInfo, IN UINT32 DimmCount, IN UINT16 *ppDimmIds, IN UINT32 pDimmIdsCount, OUT BOOLEAN *isDimmUnlocked ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 i; UINT32 j; NVDIMM_ENTRY(); if (NULL == pDimmInfo || NULL == isDimmUnlocked || (NULL == ppDimmIds && 0 < pDimmIdsCount)) { goto Finish; } *isDimmUnlocked = FALSE; if (0 == pDimmIdsCount) { for (i = 0; i < DimmCount; i++) { if (IS_DIMM_UNLOCKED(pDimmInfo[i].SecurityStateBitmask)) { *isDimmUnlocked = TRUE; break; } } } else { for (i = 0; i < pDimmIdsCount; i++) { for (j = 0; j < DimmCount; j++) { if ((ppDimmIds[i] == pDimmInfo[j].DimmID) && IS_DIMM_UNLOCKED(pDimmInfo[i].SecurityStateBitmask)) { *isDimmUnlocked = TRUE; break; } } } } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } STATIC EFI_STATUS GetPersistentMemTypeValue( IN CHAR16 *pStringValue, OUT UINT8 *pPersistentMemType ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; if (pStringValue == NULL || pPersistentMemType == NULL) { goto Finish; } if (StrICmp(pStringValue, PERSISTENT_MEM_TYPE_AD_STR) == 0) { *pPersistentMemType = PM_TYPE_AD; } else if (StrICmp(pStringValue, PERSISTENT_MEM_TYPE_AD_NI_STR) == 0) { *pPersistentMemType = PM_TYPE_AD_NI; } else { goto Finish; } ReturnCode = EFI_SUCCESS; Finish: return ReturnCode; } STATIC EFI_STATUS GetLabelVersionFromStr( IN CHAR16 *pLabelVersionStr, OUT UINT16 *pMajor, OUT UINT16 *pMinor ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 **ppSplitVersion = NULL; UINT32 NumOfElements = 0; UINT64 Major = 0; UINT64 Minor = 0; if (pLabelVersionStr == NULL || pMajor == NULL || pMinor == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ppSplitVersion = StrSplit(pLabelVersionStr, L'.', &NumOfElements); if (ppSplitVersion == NULL || NumOfElements != 2) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = CrStrDecimalToUint64(ppSplitVersion[0], &Major, FALSE); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = CrStrDecimalToUint64(ppSplitVersion[1], &Minor, FALSE); if (EFI_ERROR(ReturnCode)) { goto Finish; } *pMajor = (UINT16) Major; *pMinor = (UINT16) Minor; Finish: if (ppSplitVersion != NULL) { FreeStringArray(ppSplitVersion, NumOfElements); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check and confirm alignments using prompt Send user capacities to driver and retrieve alignments that will have to be done. Display these alignments and confirm using prompt mechanism. @param[in] pCmd command from CLI @param[in] pNvmDimmConfigProtocol is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of DIMM IDs @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[in] PersistentMemType Persistent memory type @param[in] VolatilePercent Volatile region size in percents @param[in] ReservedPercent Amount of AppDirect memory to not map in percents @param[in] ReserveDimm Reserve one DIMM for use as not interleaved AppDirect memory @param[in] UnitsToDisplay The units to be used to display capacity @param[out] pConfirmation Confirmation from prompt @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All Ok **/ STATIC EFI_STATUS CheckAndConfirmAlignments( IN struct Command *pCmd, IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds OPTIONAL, IN UINT32 SocketIdsCount, IN UINT8 PersistentMemType, IN UINT32 VolatilePercent, IN UINT32 ReservedPercent, IN UINT8 ReserveDimm, IN UINT16 UnitsToDisplay ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; COMMAND_STATUS *pCommandStatus = NULL; UINT32 VolatilePercentAligned = 0; UINT32 PercentDiff = 0; REGION_GOAL_PER_DIMM_INFO RegionConfigsInfo[MAX_DIMMS]; UINT32 RegionConfigsCount = 0; UINT32 Index = 0; CHAR16 *pSingleStatusCodeMessage = NULL; UINT32 AppDirect1Regions = 0; UINT32 AppDirect2Regions = 0; UINT32 NumOfDimmsTargeted = 0; UINT32 MaxPMInterleaveSetsPerDie = 0; NVDIMM_ENTRY(); ZeroMem(RegionConfigsInfo, sizeof(RegionConfigsInfo[0]) * MAX_DIMMS); if (pNvmDimmConfigProtocol == NULL) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on InitializeCommandStatus"); PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } /** Make a copy of user's values. They will be needed in a next call. **/ VolatilePercentAligned = VolatilePercent; ReturnCode = pNvmDimmConfigProtocol->GetActualRegionsGoalCapacities( pNvmDimmConfigProtocol, pDimmIds, DimmIdsCount, pSocketIds, SocketIdsCount, PersistentMemType, &VolatilePercentAligned, ReservedPercent, ReserveDimm, RegionConfigsInfo, &RegionConfigsCount, &NumOfDimmsTargeted, &MaxPMInterleaveSetsPerDie, pCommandStatus); if (EFI_ERROR(ReturnCode)) { if (EFI_VOLUME_CORRUPTED == ReturnCode) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_PCD_CORRUPTED); } ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_COMMAND_STATUS(pCmd->pPrintCtx, ReturnCode, CREATE_GOAL_COMMAND_STATUS_HEADER, CLI_INFO_ON, pCommandStatus); goto Finish; } if (VolatilePercent >= VolatilePercentAligned) { PercentDiff = VolatilePercent - VolatilePercentAligned; } else { PercentDiff = VolatilePercentAligned - VolatilePercent; } PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, CLI_CREATE_GOAL_PROMPT_HEADER L"\n"); PRINTER_ENABLE_TEXT_TABLE_FORMAT(pCmd->pPrintCtx); ReturnCode = ShowGoalPrintTableView(pCmd, RegionConfigsInfo, UnitsToDisplay, RegionConfigsCount, FALSE); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } else { PRINTER_PROCESS_SET_BUFFER_FORCE_TEXT_TABLE_MODE(pCmd->pPrintCtx); } if (pCommandStatus->ObjectStatusCount > 0) { PRINTER_PROMPT_COMMAND_STATUS(pCmd->pPrintCtx, EFI_SUCCESS, L"", L"", pCommandStatus); } switch (pCommandStatus->GeneralStatus) { case NVM_WARN_IMC_DDR_PMM_NOT_PAIRED: pSingleStatusCodeMessage = GetSingleNvmStatusCodeMessage(gNvmDimmCliHiiHandle, NVM_WARN_IMC_DDR_PMM_NOT_PAIRED); PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, pSingleStatusCodeMessage); FREE_POOL_SAFE(pSingleStatusCodeMessage); break; case NVM_WARN_NMFM_RATIO_LOWER_VIOLATION_1to3_6: pSingleStatusCodeMessage = GetSingleNvmStatusCodeMessage(gNvmDimmCliHiiHandle, NVM_WARN_NMFM_RATIO_LOWER_VIOLATION_1to3_6); PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, pSingleStatusCodeMessage, TWOLM_NMFM_RATIO_LOWER_3_6_STR); FREE_POOL_SAFE(pSingleStatusCodeMessage); break; case NVM_WARN_NMFM_RATIO_LOWER_VIOLATION_1to2: pSingleStatusCodeMessage = GetSingleNvmStatusCodeMessage(gNvmDimmCliHiiHandle, NVM_WARN_NMFM_RATIO_LOWER_VIOLATION_1to2); PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, pSingleStatusCodeMessage, TWOLM_NMFM_RATIO_LOWER_2_STR); FREE_POOL_SAFE(pSingleStatusCodeMessage); break; case NVM_WARN_NMFM_RATIO_UPPER_VIOLATION_1to16: pSingleStatusCodeMessage = GetSingleNvmStatusCodeMessage(gNvmDimmCliHiiHandle, NVM_WARN_NMFM_RATIO_UPPER_VIOLATION_1to16); PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, pSingleStatusCodeMessage, TWOLM_NMFM_RATIO_UPPER_16); FREE_POOL_SAFE(pSingleStatusCodeMessage); break; case NVM_WARN_NMFM_RATIO_UPPER_VIOLATION_1to8: pSingleStatusCodeMessage = GetSingleNvmStatusCodeMessage(gNvmDimmCliHiiHandle, NVM_WARN_NMFM_RATIO_UPPER_VIOLATION_1to8); PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, pSingleStatusCodeMessage, TWOLM_NMFM_RATIO_UPPER_8); FREE_POOL_SAFE(pSingleStatusCodeMessage); break; case NVM_WARN_PMTT_TABLE_NOT_FOUND: pSingleStatusCodeMessage = GetSingleNvmStatusCodeMessage(gNvmDimmCliHiiHandle, NVM_WARN_PMTT_TABLE_NOT_FOUND); PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, pSingleStatusCodeMessage); FREE_POOL_SAFE(pSingleStatusCodeMessage); break; } if (PercentDiff > PROMPT_ALIGN_PERCENTAGE) { PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, L"\n" CLI_CREATE_GOAL_PROMPT_VOLATILE L"\n"); } if ((pCommandStatus->GeneralStatus == NVM_WARN_REGION_MAX_PM_INTERLEAVE_SETS_EXCEEDED) && RegionConfigsCount > 0) { for (Index = 0; Index < RegionConfigsCount; ++Index) { if (RegionConfigsInfo[Index].AppDirectSize[APPDIRECT_1_INDEX] > 0) { AppDirect1Regions++; } if (RegionConfigsInfo[Index].AppDirectSize[APPDIRECT_2_INDEX] > 0) { AppDirect2Regions++; } } if (PersistentMemType == PM_TYPE_AD) { pSingleStatusCodeMessage = GetSingleNvmStatusCodeMessage(gNvmDimmCliHiiHandle, NVM_WARN_REGION_MAX_AD_PM_INTERLEAVE_SETS_EXCEEDED); pSingleStatusCodeMessage = CatSPrintClean(NULL, pSingleStatusCodeMessage, MaxPMInterleaveSetsPerDie, AppDirect2Regions); PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, L"\n" FORMAT_STR_NL, pSingleStatusCodeMessage); FREE_POOL_SAFE(pSingleStatusCodeMessage); } if (PersistentMemType == PM_TYPE_AD_NI) { pSingleStatusCodeMessage = GetSingleNvmStatusCodeMessage(gNvmDimmCliHiiHandle, NVM_WARN_REGION_MAX_AD_NI_PM_INTERLEAVE_SETS_EXCEEDED); pSingleStatusCodeMessage = CatSPrintClean(NULL, pSingleStatusCodeMessage, MaxPMInterleaveSetsPerDie, (NumOfDimmsTargeted - AppDirect1Regions)); PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, L"\n" FORMAT_STR_NL, pSingleStatusCodeMessage); FREE_POOL_SAFE(pSingleStatusCodeMessage); } } Finish: FreeCommandStatus(&pCommandStatus); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Execute the Create Goal command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action @retval EFI_NO_RESPONSE FW busy for one or more dimms **/ EFI_STATUS CreateGoal( IN struct Command *pCmd ) { EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; COMMAND_STATUS *pCommandStatus = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; BOOLEAN Force = FALSE; CHAR16 *pTargetValue = NULL; CHAR16 *pPropertyValue = NULL; UINT16 *pDimmIds = NULL; UINT32 DimmIdsCount = 0; UINT16 *pSocketIds = NULL; UINT32 SocketIdsCount = 0; UINT64 PropertyValue = 0; UINT32 VolatileMode = 0; UINT32 ReservedPercent = 0; UINT8 ReserveDimm = RESERVE_DIMM_NONE; UINT8 PersistentMemType = PM_TYPE_AD; COMMAND_INPUT ShowGoalCmdInput; COMMAND ShowGoalCmd; BOOLEAN Confirmation = FALSE; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; BOOLEAN Valid = FALSE; UINT16 UnitsOption = DISPLAY_SIZE_UNIT_UNKNOWN; UINT16 UnitsToDisplay = FixedPcdGet16(PcdDcpmmCliDefaultCapacityUnit); CHAR16 *pUnitsStr = NULL; CHAR16 *pCommandStr = NULL; DISPLAY_PREFERENCES DisplayPreferences; UINT16 LabelVersionMajor = 0; UINT16 LabelVersionMinor = 0; INTEL_DIMM_CONFIG *pIntelDIMMConfig = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pShowGoalOutputArgs = NULL; CHAR16 *pSingleStatusCodeMessage = NULL; UINT32 MaxPMInterleaveSetsPerDie = 0; BOOLEAN isDimmUnlocked = FALSE; NVDIMM_ENTRY(); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); ZeroMem(&ShowGoalCmdInput, sizeof(ShowGoalCmdInput)); ZeroMem(&ShowGoalCmd, sizeof(ShowGoalCmd)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; // NvmDimmConfigProtocol required ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_SECURITY, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } if (containsOption(pCmd, FORCE_OPTION) || containsOption(pCmd, FORCE_OPTION_SHORT) || XML == pPrinterCtx->FormatType) { Force = TRUE; } ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } UnitsToDisplay = DisplayPreferences.SizeUnit; ReturnCode = GetUnitsOption(pCmd, &UnitsOption); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Any valid units option will override the preferences **/ if (UnitsOption != DISPLAY_SIZE_UNIT_UNKNOWN) { UnitsToDisplay = UnitsOption; } // check if auto provision is enabled RetrieveIntelDIMMConfig(&pIntelDIMMConfig); if(pIntelDIMMConfig != NULL) { if (pIntelDIMMConfig->ProvisionCapacityMode == PROVISION_CAPACITY_MODE_AUTO) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_CREATE_GOAL_AUTO_PROV_ENABLED); FreePool(pIntelDIMMConfig); goto Finish; } else { FreePool(pIntelDIMMConfig); } } // check targets if (ContainTarget(pCmd, DIMM_TARGET)) { pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetDimmIdsFromString"); goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsCount)){ ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } } if (ContainTarget(pCmd, SOCKET_TARGET)) { pTargetValue = GetTargetValue(pCmd, SOCKET_TARGET); ReturnCode = GetUintsFromString(pTargetValue, &pSocketIds, &SocketIdsCount); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_TARGET_SOCKET); NVDIMM_DBG("Failed on GetSocketsFromString"); goto Finish; } } // initialize status structure ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on InitializeCommandStatus"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } ReturnCode = ContainsProperty(pCmd, MEMORY_MODE_PROPERTY); if (!EFI_ERROR(ReturnCode)) { Valid = PropertyToUint64(pCmd, MEMORY_MODE_PROPERTY, &PropertyValue); /** Make sure it is in 0-100 percent range. **/ if (Valid && PropertyValue <= 100) { VolatileMode = (UINT32)PropertyValue; } else { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_PROPERTY_MEMORY_MODE); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } else { VolatileMode = 0; } if ((ReturnCode = ContainsProperty(pCmd, PERSISTENT_MEM_TYPE_PROPERTY)) != EFI_NOT_FOUND) { if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } ReturnCode = GetPropertyValue(pCmd, PERSISTENT_MEM_TYPE_PROPERTY, &pPropertyValue); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } ReturnCode = GetPersistentMemTypeValue(pPropertyValue, &PersistentMemType); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_PROPERTY_PERSISTENT_MEM_TYPE); goto Finish; } } ReturnCode = ContainsProperty(pCmd, RESERVED_PROPERTY); if (!EFI_ERROR(ReturnCode)) { Valid = PropertyToUint64(pCmd, RESERVED_PROPERTY, &PropertyValue); /** Make sure it is in 0-100 percent range. **/ if (Valid && PropertyValue <= 100) { ReservedPercent = (UINT32)PropertyValue; } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_PROPERTY_RESERVED); goto Finish; } } else { ReservedPercent = 0; } if (ReservedPercent + VolatileMode > 100) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_PROPERTIES_MEMORYMODE_RESERVED_TOO_LARGE); goto Finish; } /** If Volatile and Reserved Percent sum to 100 then never map Appdirect even if alignment would allow it **/ if (ReservedPercent + VolatileMode == 100) { PersistentMemType = PM_TYPE_RESERVED; } ReturnCode = ContainsProperty(pCmd, NS_LABEL_VERSION_PROPERTY); if (!EFI_ERROR(ReturnCode)) { ReturnCode = GetPropertyValue(pCmd, NS_LABEL_VERSION_PROPERTY, &pPropertyValue); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = GetLabelVersionFromStr(pPropertyValue, &LabelVersionMajor, &LabelVersionMinor); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } if (LabelVersionMajor != NSINDEX_MAJOR) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_PROPERTY_NS_LABEL_VERSION); goto Finish; } if ((LabelVersionMinor != NSINDEX_MINOR_1) && (LabelVersionMinor != NSINDEX_MINOR_2)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_PROPERTY_NS_LABEL_VERSION); goto Finish; } } else if (ReturnCode == EFI_NOT_FOUND) { /** Default to 1.2 labels **/ LabelVersionMajor = NSINDEX_MAJOR; LabelVersionMinor = NSINDEX_MINOR_2; } else { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } if (!Force) { ReturnCode = CheckAndConfirmAlignments(pCmd, pNvmDimmConfigProtocol, pDimmIds, DimmIdsCount, pSocketIds, SocketIdsCount, PersistentMemType, VolatileMode, ReservedPercent, ReserveDimm, UnitsToDisplay); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = AreRequestedDimmsSecurityUnlocked(pDimms, DimmCount, pDimmIds, DimmIdsCount, &isDimmUnlocked); if (EFI_ERROR(ReturnCode)) { goto Finish; } // send warning if security unlocked for target dimms if (isDimmUnlocked) { PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, CLI_WARN_GOAL_CREATION_SECURITY_UNLOCKED); } ReturnCode = PromptYesNo(&Confirmation); if (EFI_ERROR(ReturnCode)) { PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_PROMPT_INVALID); NVDIMM_DBG("Failed on PromptedInput"); goto Finish; } else if (!Confirmation) { goto Finish; } } ReturnCode = pNvmDimmConfigProtocol->CreateGoalConfig(pNvmDimmConfigProtocol, FALSE, pDimmIds, DimmIdsCount, pSocketIds, SocketIdsCount, PersistentMemType, VolatileMode, ReservedPercent, ReserveDimm, LabelVersionMajor, LabelVersionMinor, &MaxPMInterleaveSetsPerDie, pCommandStatus); if (!EFI_ERROR(ReturnCode)) { ReturnCode = CreateCmdLineOutputStr(pCmd, &pShowGoalOutputArgs); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } if (UnitsOption != DISPLAY_SIZE_UNIT_UNKNOWN) { CHECK_RESULT(UnitsToStr(gNvmDimmCliHiiHandle, UnitsToDisplay, &pUnitsStr), Finish); pCommandStr = CatSPrintClean(pCommandStr, FORMAT_STR_SPACE FORMAT_STR FORMAT_STR L" " FORMAT_STR L" " FORMAT_STR, SHOW_VERB, pShowGoalOutputArgs, UNITS_OPTION, pUnitsStr, GOAL_TARGET); } else { pCommandStr = CatSPrintClean(pCommandStr, FORMAT_STR_SPACE FORMAT_STR FORMAT_STR, SHOW_VERB, pShowGoalOutputArgs, GOAL_TARGET); } FillCommandInput(pCommandStr, &ShowGoalCmdInput); ReturnCode = Parse(&ShowGoalCmdInput, &ShowGoalCmd); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed parsing command input"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } if (ShowGoalCmd.run == NULL) { NVDIMM_WARN("Couldn't find show -goal command"); ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } if (!Force) { PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, CLI_CREATE_SUCCESS_STATUS); } ExecuteCmd(&ShowGoalCmd); FREE_POOL_SAFE(pCommandStr); if (pCommandStatus->GeneralStatus == NVM_WARN_REGION_AD_NI_PM_INTERLEAVE_SETS_REDUCED) { pSingleStatusCodeMessage = GetSingleNvmStatusCodeMessage(gNvmDimmCliHiiHandle, NVM_WARN_REGION_AD_NI_PM_INTERLEAVE_SETS_REDUCED); pSingleStatusCodeMessage = CatSPrintClean(NULL, pSingleStatusCodeMessage, MaxPMInterleaveSetsPerDie); PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, pSingleStatusCodeMessage); FREE_POOL_SAFE(pSingleStatusCodeMessage); } goto FinishSkipPrinterProcess; } else { ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, CREATE_GOAL_COMMAND_STATUS_HEADER, CLI_INFO_ON, pCommandStatus); } Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FinishSkipPrinterProcess: FreeCommandInput(&ShowGoalCmdInput); FreeCommandStructure(&ShowGoalCmd); FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pCommandStr); FREE_POOL_SAFE(pSocketIds); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pShowGoalOutputArgs); FREE_POOL_SAFE(pUnitsStr); FREE_POOL_SAFE(pCommandStr); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the create dimm command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterCreateGoalCommand( ) { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&CreateGoalCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/CreateGoalCommand.h000066400000000000000000000025201440615110200213640ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _CREATE_GOAL_COMMAND_ #define _CREATE_GOAL_COMMAND_ #include // Cli will warn the user when suggested alignment is greater than this percentage #define PROMPT_ALIGN_PERCENTAGE 10 /** Traverse targeted dimms to determine if Security is enabled in Unlocked state @param[in] SecurityFlag Security mask from FW @retval TRUE if at least one targeted dimm has security enabled in unlocked state @retval FALSE if none of targeted dimms have security enabled in unlocked state **/ EFI_STATUS EFIAPI AreRequestedDimmsSecurityUnlocked( IN DIMM_INFO *pDimmInfo, IN UINT32 DimmCount, IN UINT16 *ppDimmIds, IN UINT32 pDimmIdsCount, OUT BOOLEAN *isDimmUnlocked ); /** Register the Create Goal command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterCreateGoalCommand(); /** Execute the Create Goal command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS CreateGoal( IN struct Command *pCmd ); #endif /** _CREATE_GOAL_COMMAND_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/CreateNamespaceCommand.h000066400000000000000000000017361440615110200224060ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _CREATE_NAMESPACE_COMMAND_H_ #define _CREATE_NAMESPACE_COMMAND_H_ #include #include #include #include #include #include #include #include "Common.h" /** Register the create namespace command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterCreateNamespaceCommand( ); /** Execute the create namespace command @param[in] pCmd command from CLI @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS CreateNamespaceCmd( IN struct Command *pCmd ); #endif /** _CREATE_NAMESPACE_COMMAND_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/DeleteDimmCommand.c000066400000000000000000000302611440615110200213650ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef OS_BUILD #include #include #include #include #include #include #include #include "DeleteDimmCommand.h" #include "SetDimmCommand.h" #include "Common.h" /** Command syntax definition **/ struct Command DeleteDimmCommand = { DELETE_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"", HELP_VERBOSE_DETAILS_TEXT,FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {FORCE_OPTION_SHORT, FORCE_OPTION, L"", L"",HELP_FORCE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", SOURCE_OPTION, L"", SOURCE_OPTION_HELP, L"Source of Passphrase file",FALSE, ValueRequired}, {L"", MASTER_OPTION, L"", L"", L"Master Passphrase",FALSE, ValueEmpty}, {L"", DEFAULT_OPTION, L"", L"", L"Default settings",FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, {{DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, TRUE, ValueOptional}}, //!< targets {{PASSPHRASE_PROPERTY, L"", HELP_TEXT_STRING, FALSE, ValueOptional}}, //!< properties L"Erase persistent data on one or more " PMEM_MODULES_STR L".", //!< help DeleteDimm, TRUE }; /** Execute the delete dimm command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS DeleteDimm( IN struct Command *pCmd ) { EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; CHAR16 *pLoadUserPath = NULL; CHAR16 *pLoadFilePath = NULL; CHAR16 *pPassphrase = NULL; CHAR16 *pPassphraseStatic = NULL; CHAR16 *pTargetValue = NULL; UINT16 *pDimmIds = NULL; UINT32 DimmHandle = 0; UINT32 DimmIndex = 0; UINT16 Index = 0; UINT32 DimmIdsCount = 0; EFI_DEVICE_PATH_PROTOCOL *pDevicePathProtocol = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; COMMAND_STATUS *pCommandStatus = NULL; BOOLEAN Force = FALSE; BOOLEAN Confirmation = FALSE; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; PRINT_CONTEXT *pPrinterCtx = NULL; BOOLEAN MasterOptionSpecified = FALSE; BOOLEAN DefaultOptionSpecified = FALSE; UINT16 SecurityOperation = SECURITY_OPERATION_UNDEFINED; DIMM_INFO DimmInfo; NVDIMM_ENTRY(); ZeroMem(DimmStr, sizeof(DimmStr)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_SECURITY, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } // initialize status structure ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } // check targets if (ContainTarget(pCmd, DIMM_TARGET)) { pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetDimmIdsFromString"); goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsCount)){ ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } } /** If no dimm IDs are specified get IDs from all dimms **/ if (DimmIdsCount == 0) { ReturnCode = GetManageableDimmsNumberAndId(pNvmDimmConfigProtocol, FALSE, &DimmIdsCount, &pDimmIds); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } if (DimmIdsCount == 0) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_INFO_NO_MANAGEABLE_DIMMS); goto Finish; } } /** Check master option **/ MasterOptionSpecified = containsOption(pCmd, MASTER_OPTION); /** Check default option **/ DefaultOptionSpecified = containsOption(pCmd, DEFAULT_OPTION); /** Check if default option is supported on selected modules **/ if (DefaultOptionSpecified) { for (Index = 0; Index < DimmIdsCount; Index++) { CHECK_RESULT((pNvmDimmConfigProtocol->GetDimm(pNvmDimmConfigProtocol, pDimmIds[Index], DIMM_INFO_CATEGORY_NONE, &DimmInfo)), Finish); if (IsDefaultMasterPassphraseRestricted(DimmInfo)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DEFAULT_NOT_SUPPORTED_FIRMWARE_REV); goto Finish; } } } /** Check force option **/ if (containsOption(pCmd, FORCE_OPTION) || containsOption(pCmd, FORCE_OPTION_SHORT)) { Force = TRUE; } if (MasterOptionSpecified) { // FALSE = Master passphrase must be enabled for FIS >= 3.2 PMem modules as well if (!AllDimmsInListHaveMasterPassphraseEnabled(pDimms, DimmCount, pDimmIds, DimmIdsCount, FALSE)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_MASTER_PASSPHRASE_NOT_ENABLED); goto Finish; } } ReturnCode = GetPropertyValue(pCmd, PASSPHRASE_PROPERTY, &pPassphraseStatic); if (EFI_ERROR(ReturnCode)) { if (ReturnCode != EFI_NOT_FOUND) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } } if ((pPassphraseStatic != NULL) && (containsOption(pCmd, SOURCE_OPTION)) && (StrCmp(pPassphraseStatic, L"") != 0)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNSUPPORTED_COMMAND_SYNTAX); goto Finish; } // Check -source option if ((pPassphraseStatic != NULL) && (containsOption(pCmd, SOURCE_OPTION)) && (StrCmp(pPassphraseStatic, L"") == 0)) { pLoadUserPath = getOptionValue(pCmd, SOURCE_OPTION); if (pLoadUserPath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Could not get -source value. Out of memory"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = CheckMasterAndDefaultOptions(pCmd, TRUE, MasterOptionSpecified, DefaultOptionSpecified); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (MasterOptionSpecified) { SecurityOperation = SECURITY_OPERATION_MASTER_ERASE_DEVICE; } else { SecurityOperation = SECURITY_OPERATION_ERASE_DEVICE; } pLoadFilePath = AllocateZeroPool(OPTION_VALUE_LEN * sizeof(*pLoadFilePath)); if (pLoadFilePath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = GetDeviceAndFilePath(pLoadUserPath, pLoadFilePath, &pDevicePathProtocol); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get file path (" FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } ReturnCode = ParseSourcePassFile(pCmd, pLoadFilePath, pDevicePathProtocol, &pPassphrase, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } // Check if prompt } else if ((pPassphraseStatic != NULL) && (StrCmp(pPassphraseStatic, L"") == 0)) { ReturnCode = CheckMasterAndDefaultOptions(pCmd, TRUE, MasterOptionSpecified, DefaultOptionSpecified); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (MasterOptionSpecified) { SecurityOperation = SECURITY_OPERATION_MASTER_ERASE_DEVICE; } else { SecurityOperation = SECURITY_OPERATION_ERASE_DEVICE; } ReturnCode = PromptedInput(L"Enter passphrase:\n", FALSE, FALSE, &pPassphrase); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_PROMPT_INVALID); NVDIMM_DBG("Failed on PromptedInput"); goto Finish; } } else if (pPassphraseStatic != NULL) { // Passphrase provided ReturnCode = CheckMasterAndDefaultOptions(pCmd, TRUE, MasterOptionSpecified, DefaultOptionSpecified); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (MasterOptionSpecified) { SecurityOperation = SECURITY_OPERATION_MASTER_ERASE_DEVICE; } else { SecurityOperation = SECURITY_OPERATION_ERASE_DEVICE; } pPassphrase = CatSPrint(NULL, FORMAT_STR, pPassphraseStatic); } else { // Passphrase not provided ReturnCode = CheckMasterAndDefaultOptions(pCmd, FALSE, MasterOptionSpecified, DefaultOptionSpecified); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (MasterOptionSpecified) { SecurityOperation = SECURITY_OPERATION_MASTER_ERASE_DEVICE; } else { SecurityOperation = SECURITY_OPERATION_ERASE_DEVICE; } // At this point an empty string has already been handled and we are now assuming passphrase // was not given because security is disabled and passphrase doesn't matter OR the secure erase // request is for the default master passphrase and an empty string is passed in for the default // master passphrase pPassphrase = CatSPrint(NULL, L""); } /** Ask for prompt when Force option is not given **/ if (!Force) { for (Index = 0; Index < DimmIdsCount; Index++) { ReturnCode = GetDimmHandleByPid(pDimmIds[Index], pDimms, DimmCount, &DimmHandle, &DimmIndex); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = GetPreferredDimmIdAsString(DimmHandle, pDimms[DimmIndex].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, L"Erasing " PMEM_MODULE_STR L" " FORMAT_STR L".", DimmStr); ReturnCode = PromptYesNo(&Confirmation); if (!EFI_ERROR(ReturnCode) && Confirmation) { ReturnCode = pNvmDimmConfigProtocol->SetSecurityState(pNvmDimmConfigProtocol,&pDimmIds[Index], 1, SecurityOperation, pPassphrase, NULL, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto FinishCommandStatusSet; } } else { PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, L"Skipped erasing data from " PMEM_MODULE_STR L" " FORMAT_STR L"\n", DimmStr); continue; } } } else { ReturnCode = pNvmDimmConfigProtocol->SetSecurityState(pNvmDimmConfigProtocol, pDimmIds, DimmIdsCount, SecurityOperation, pPassphrase, NULL, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto FinishCommandStatusSet; } } FinishCommandStatusSet: ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, ERASE_STR, L"", pCommandStatus); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); CleanUnicodeStringMemory(pPassphrase); CleanUnicodeStringMemory(pPassphraseStatic); FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pPassphrase); FREE_POOL_SAFE(pLoadFilePath); FREE_POOL_SAFE(pLoadUserPath); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the delete dimm command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterDeleteDimmCommand( ) { EFI_STATUS rc; NVDIMM_ENTRY(); rc = RegisterCommand(&DeleteDimmCommand); NVDIMM_EXIT_I64(rc); return rc; } #endif /** OS_BUILD **/ ipmctl-03.00.00.0485/DcpmPkg/cli/DeleteDimmCommand.h000066400000000000000000000015241440615110200213720ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef OS_BUILD #ifndef _DELETE_DIMM_COMMAND_ #define _DELETE_DIMM_COMMAND_ #include #include "CommandParser.h" #define ERASE_STR L"Erase" /** Register the delete dimm command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterDeleteDimmCommand(); /** Execute the delete dimm command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS DeleteDimm( IN struct Command *pCmd ); #endif /** _DELETE_DIMM_COMMAND_ **/ #endif /** OS_BUILD **/ipmctl-03.00.00.0485/DcpmPkg/cli/DeleteGoalCommand.c000066400000000000000000000130061440615110200213570ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include "Debug.h" #include "Types.h" #include "Utility.h" #include "NvmInterface.h" #include "CommandParser.h" #include "DeleteGoalCommand.h" #include "Common.h" /** Command syntax definition **/ struct Command DeleteGoalCommand = { DELETE_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, { L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, { L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, { L"", LARGE_PAYLOAD_OPTION, L"", L"",HELP_LPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, { L"", SMALL_PAYLOAD_OPTION, L"", L"",HELP_SPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #else {L"", L"", L"", L"", L"",FALSE, ValueOptional} #endif }, { //!< targets {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional}, {GOAL_TARGET, L"", L"", TRUE, ValueEmpty}, {SOCKET_TARGET, L"", HELP_TEXT_SOCKET_IDS, FALSE, ValueOptional} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Delete the region configuration goal from one or more " PMEM_MODULES_STR L".", //!< help DeleteGoal, TRUE, //!< enable print control support }; /** Execute the Delete Goal command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action @retval EFI_NO_RESPONSE FW busy for one or more dimms **/ EFI_STATUS DeleteGoal( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; COMMAND_STATUS *pCommandStatus = NULL; CHAR16 *pTargetValue = NULL; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; UINT16 *pDimmIds = NULL; UINT32 DimmIdsCount = 0; UINT16 *pSocketIds = NULL; UINT32 SocketIdsCount = 0; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; PRINT_CONTEXT *pPrinterCtx = NULL; NVDIMM_ENTRY(); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; /** Need NvmDimmConfigProtocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } if (ContainTarget(pCmd, DIMM_TARGET)) { pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetDimmIdsFromString"); goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsCount)){ ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } } if (ContainTarget(pCmd, SOCKET_TARGET)) { pTargetValue = GetTargetValue(pCmd, SOCKET_TARGET); ReturnCode = GetUintsFromString(pTargetValue, &pSocketIds, &SocketIdsCount); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_TARGET_SOCKET); NVDIMM_DBG("Failed on GetSocketsFromString"); goto Finish; } } // initialize status structure ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->DeleteGoalConfig(pNvmDimmConfigProtocol, pDimmIds, DimmIdsCount, pSocketIds, SocketIdsCount, pCommandStatus); if (EFI_VOLUME_CORRUPTED == ReturnCode) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_PCD_CORRUPTED); } ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, L"Delete memory allocation goal", L" from", pCommandStatus); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pSocketIds); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the Delete Goal command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterDeleteGoalCommand( ) { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&DeleteGoalCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/DeleteGoalCommand.h000066400000000000000000000013351440615110200213660ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DELETE_GOAL_COMMAND_ #define _DELETE_GOAL_COMMAND_ #include /** Register the Delete Goal command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterDeleteGoalCommand(); /** Execute the Delete Goal command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS DeleteGoal( IN struct Command *pCmd ); #endif /** _DELETE_GOAL_COMMAND_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/DeleteNamespaceCommand.h000066400000000000000000000015351440615110200224020ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DELETE_NAMESPACE_COMMAND_H_ #define _DELETE_NAMESPACE_COMMAND_H_ #include #include "Common.h" /** Register the delete namespace command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterDeleteNamespaceCommand( ); /** Execute the delete namespace command @param[in] pCmd command from CLI @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS DeleteNamespaceCmd( IN struct Command *pCmd ); #endif /** _DELETE_NAMESPACE_COMMAND_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/DeletePcdCommand.c000066400000000000000000000255641440615110200212170ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "CommandParser.h" #include "Common.h" #include "DeletePcdCommand.h" #include /** Command syntax definition **/ struct Command DeletePcdCommand = { DELETE_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, { L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, { L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, { L"", LARGE_PAYLOAD_OPTION, L"", L"",HELP_LPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, { L"", SMALL_PAYLOAD_OPTION, L"", L"",HELP_SPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, {FORCE_OPTION_SHORT, FORCE_OPTION, L"", L"",HELP_FORCE_DETAILS_TEXT, FALSE, ValueEmpty} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, { //!< targets {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional}, {PCD_TARGET, L"", PCD_CONFIG_TARGET_VALUE #ifndef OS_BUILD L"|" PCD_LSA_TARGET_VALUE #endif , TRUE, ValueOptional} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Clear the namespace LSA partition on one or more " PMEM_MODULES_STR L".", //!< help DeletePcdCmd, TRUE //!< enable print control support }; STATIC EFI_STATUS ValidatePcdTarget( IN CHAR16 *pTargetValue, IN CHAR16 *pExpectedTargetValue ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pTargetValue == NULL || pExpectedTargetValue == NULL) { goto Finish; } if (StrICmp(pTargetValue, pExpectedTargetValue) != 0) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Execute the Delete PCD command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_FOUND couldn't open Config Protocol @retval EFI_ABORTED internal **/ EFI_STATUS DeletePcdCmd( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_STATUS TempReturnCode = EFI_SUCCESS; COMMAND_STATUS *pCommandStatus = NULL; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; UINT16 *pDimmIds = NULL; UINT16 DimmIdsWithNamespaces[MAX_DIMMS]; UINT32 DimmIdsWithNamespacesCount = 0; UINT32 DimmIdsCount = 0; DIMM_INFO *pDimms = NULL; DIMM_INFO *pDimm = NULL; UINT32 DimmCount = 0; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; CHAR16 *pTargetValue = NULL; UINT32 DimmIndex = 0; UINT32 DimmHandle = 0; UINT32 Index = 0; UINT32 Index2 = 0; UINT32 Attempts = 0; UINT32 Successes = 0; BOOLEAN Force = FALSE; BOOLEAN DimmInNamespace = FALSE; BOOLEAN Confirmation = FALSE; CHAR16 *pCommandStatusMessage = NULL; UINT32 ConfigIdMask = 0; CHAR16 *pDisplayTargets = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; NVDIMM_ENTRY(); SetDisplayInfo(L"DeletePcd", ResultsView, NULL); ZeroMem(DimmStr, sizeof(DimmStr)); ZeroMem(DimmIdsWithNamespaces, sizeof(DimmIdsWithNamespaces)); if (pCmd == NULL) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; /** Get config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_INFO_NO_DIMMS); } goto Finish; } /** Check force option **/ if (containsOption(pCmd, FORCE_OPTION) || containsOption(pCmd, FORCE_OPTION_SHORT)) { Force = TRUE; } ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (ContainTarget(pCmd, DIMM_TARGET)) { pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsCount); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsCount)){ PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNMANAGEABLE_DIMM); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } pTargetValue = GetTargetValue(pCmd, PCD_TARGET); if (EFI_SUCCESS == ValidatePcdTarget(pTargetValue, PCD_LSA_TARGET_VALUE)) { #ifdef OS_BUILD ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_TARGET_PCD); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"LSA option not available. Refer to your OSV tools for " PMEM_MODULE_STR L" namespace management."); goto Finish; #endif ConfigIdMask |= DELETE_PCD_CONFIG_LSA_MASK; } if (EFI_SUCCESS == ValidatePcdTarget(pTargetValue, PCD_CONFIG_TARGET_VALUE)) { ConfigIdMask |= DELETE_PCD_CONFIG_CIN_MASK | DELETE_PCD_CONFIG_COUT_MASK | DELETE_PCD_CONFIG_CCUR_MASK; } if (0 == ConfigIdMask) { #ifdef OS_BUILD ConfigIdMask |= DELETE_PCD_CONFIG_CIN_MASK | DELETE_PCD_CONFIG_COUT_MASK | DELETE_PCD_CONFIG_CCUR_MASK; #else ConfigIdMask |= DELETE_PCD_CONFIG_ALL_MASK; #endif } /* If no dimms specified then use all dimms */ if (DimmIdsCount == 0) { FREE_POOL_SAFE(pDimmIds); pDimmIds = AllocateZeroPool(sizeof(*pDimmIds) * DimmCount); if (pDimmIds == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } for (Index =0; Index < DimmCount; Index++) { pDimmIds[Index] = pDimms[Index].DimmID; } DimmIdsCount = DimmCount; } if (ConfigIdMask & (DELETE_PCD_CONFIG_CIN_MASK | DELETE_PCD_CONFIG_COUT_MASK | DELETE_PCD_CONFIG_CCUR_MASK)) { pDisplayTargets = CatSPrint(pDisplayTargets, L"Config "); if (NULL == pDisplayTargets) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } } if (ConfigIdMask & (DELETE_PCD_CONFIG_LSA_MASK)) { if (pDisplayTargets) { pDisplayTargets = CatSPrint(pDisplayTargets, L"& "); if (NULL == pDisplayTargets) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } } pDisplayTargets = CatSPrint(pDisplayTargets, L"LSA "); if (NULL == pDisplayTargets) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } } pCommandStatusMessage = CatSPrint(NULL, L"Clear " FORMAT_STR L"partition(s)", pDisplayTargets); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_NOT_STARTED); if (!Force) { PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, L"WARNING: Modifying the Platform Configuration Data can result in loss of data!\n"); PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, L"Clear " FORMAT_STR L"partition(s) on %d " PMEM_MODULE_STR L"(s).", pDisplayTargets, DimmIdsCount); ReturnCode = PromptYesNo(&Confirmation); if (EFI_ERROR(ReturnCode) || !Confirmation) { ReturnCode = EFI_NOT_STARTED; goto Finish; } } #ifdef _WINDOWS ReturnCode = GetDimmIdsWithNamespaces(DimmIdsWithNamespaces, &DimmIdsWithNamespacesCount, MAX_DIMMS); if (EFI_ERROR(ReturnCode)) { goto Finish; } #endif PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, L"\n"); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_NOT_STARTED); for (Index = 0; Index < DimmIdsCount; Index++) { Attempts++; ReturnCode = GetDimmHandleByPid(pDimmIds[Index], pDimms, DimmCount, &DimmHandle, &DimmIndex); if (EFI_ERROR(ReturnCode)) { goto Finish; } pDimm = &pDimms[DimmIndex]; ReturnCode = GetPreferredDimmIdAsString(pDimm->DimmHandle, pDimm->DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } DimmInNamespace = FALSE; for (Index2 = 0; Index2 < DimmIdsWithNamespacesCount; Index2++) { if (DimmIdsWithNamespaces[Index2] == pDimm->DimmHandle) { DimmInNamespace = TRUE; break; } } if (DimmInNamespace) { PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, PMEM_MODULE_STR L" " FORMAT_STR L" is a member of a Namespace. Will not delete data from this " PMEM_MODULE_STR L".", DimmStr); SetObjStatusForDimmInfoWithErase(pCommandStatus, pDimm, NVM_ERR_PCD_DELETE_DENIED, TRUE); } else { pCommandStatus->GeneralStatus = NVM_ERR_OPERATION_NOT_STARTED; TempReturnCode = pNvmDimmConfigProtocol->ModifyPcdConfig(pNvmDimmConfigProtocol, &pDimmIds[Index], 1, ConfigIdMask, pCommandStatus); if (EFI_ERROR(TempReturnCode)) { ReturnCode = TempReturnCode; } else { Successes++; } } } if (Attempts > 0 && Attempts == Successes) { pCommandStatus->GeneralStatus = NVM_SUCCESS; } else if (pCommandStatus->GeneralStatus == NVM_SUCCESS) { pCommandStatus->GeneralStatus = NVM_ERR_OPERATION_FAILED; } ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, L"Clear partition(s)", L" on", pCommandStatus); } else { PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, pCommandStatusMessage, L" on", pCommandStatus); } if (Successes > 0) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"\nData dependencies may result in other commands being affected. A system reboot is required before all changes will take effect."); } Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pCommandStatusMessage); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pDisplayTargets); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the Delete PCD command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterDeletePcdCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&DeletePcdCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/DeletePcdCommand.h000066400000000000000000000012101440615110200212020ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DELETE_PCD_COMMAND_ #define _DELETE_PCD_COMMAND_ /** Register the Delete PCD command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterDeletePcdCommand( ); /** Execute the Delete PCD command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters **/ EFI_STATUS DeletePcdCmd( IN struct Command *pCmd ); #endif /* _DELETE_PCD_COMMAND_ */ ipmctl-03.00.00.0485/DcpmPkg/cli/DumpDebugCommand.c000066400000000000000000000254521440615110200212360ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include "DumpDebugCommand.h" #include "NvmDimmCli.h" #include "NvmInterface.h" #include "LoadCommand.h" #include "Debug.h" #include "Convert.h" #include "Nlog.h" /** Get FW debug log syntax definition **/ struct Command DumpDebugCommandSyntax = { DUMP_VERB, //!< verb { //!< options {L"", DESTINATION_PREFIX_OPTION, L"", L"", DESTINATION_PREFIX_OPTION_HELP, FALSE, ValueRequired }, {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", LARGE_PAYLOAD_OPTION, L"", L"",HELP_LPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", SMALL_PAYLOAD_OPTION, L"", L"",HELP_SPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT,FALSE, ValueRequired }, #endif { L"", DICTIONARY_OPTION, L"", DICTIONARY_OPTION_HELP, L"Dictionary to interpret the log",FALSE, ValueOptional } }, { {DEBUG_TARGET, L"", L"", TRUE, ValueEmpty}, {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Dump firmware debug log.", //!< help DumpDebugCommand, TRUE //!< run function }; /** Register syntax of dump -debug **/ EFI_STATUS RegisterDumpDebugCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&DumpDebugCommandSyntax); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Dump debug log command @param[in] pCmd command from CLI @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS DumpDebugCommand( IN struct Command *pCmd ) { EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; COMMAND_STATUS *pCommandStatus = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 DimmCount = 0; UINT16 *pDimmIds = NULL; UINT32 DimmIdsNum = 0; CHAR16 *pTargetValue = NULL; CHAR16 *pDumpUserPath = NULL; DIMM_INFO *pDimms = NULL; UINT32 Index = 0; nlog_dict_entry * next; BOOLEAN dictExists = FALSE; CHAR16 *pDictUserPath = NULL; CHAR16 *raw_file_name = NULL; CHAR16 *decoded_file_name = NULL; nlog_dict_entry* dict_head = NULL; UINT32 dict_version; UINT64 dict_entries; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *SourceNames[NUM_FW_DEBUG_LOG_SOURCES] = {L"media", L"sram", L"spi"}; UINT8 IndexSource = 0; VOID *RawLogBuffer = NULL; UINT64 RawLogBufferSizeBytes = 0; INT8 SuccessesPerDimm[MAX_DIMMS]; UINT32 Reserved = 0; NVDIMM_ENTRY(); // Make sure SuccessesPerDimm is fully initialized to -1's. // -1 indicates a dimm that ends up to not be specified // so we don't care about its number of successes SetMem(SuccessesPerDimm, MAX_DIMMS * sizeof(SuccessesPerDimm[0]), (UINT8)(-1)); if (pCmd == NULL) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; /** Open Config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } ReturnCode = GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } if (ContainTarget(pCmd, DIMM_TARGET)) { /** get specific DIMM pid passed in, set it **/ pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsNum); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Target value is not a valid Dimm ID"); goto Finish; } } // Check -destination option if (containsOption(pCmd, DESTINATION_OPTION)) { pDumpUserPath = getOptionValue(pCmd, DESTINATION_OPTION); if (pDumpUserPath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Could not get -destination value. Out of memory"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_OUT_OF_MEMORY); goto Finish; } } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_PARSER_DETAILED_ERR_OPTION_REQUIRED, DESTINATION_OPTION); goto Finish; } if (containsOption(pCmd, DICTIONARY_OPTION)) { pDictUserPath = getOptionValue(pCmd, DICTIONARY_OPTION); if (pDictUserPath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Could not get -dict value. Out of memory"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_OUT_OF_MEMORY); goto Finish; } if (EFI_ERROR(FileExists(pDictUserPath, &dictExists))) { ReturnCode = EFI_END_OF_FILE; NVDIMM_ERR("Could not check for existence of the dictionary file"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_INTERNAL_ERROR); goto Finish; } if (!dictExists) { ReturnCode = EFI_LOAD_ERROR; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"The passed dictionary file doesn't exist\n"); goto Finish; } } // Only load the dictionary once if (dictExists) { dict_head = load_nlog_dict(pCmd, pDictUserPath, &dict_version, &dict_entries); if (!dict_head) { ReturnCode = EFI_LOAD_ERROR; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to load the dictionary file " FORMAT_STR L"\n", pDictUserPath); goto Finish; } PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Loaded %d dictionary entries\n", dict_entries); } for (Index = 0; Index < DimmCount; Index++) { // Initialize to -1 so we can ignore dimms that aren't specified SuccessesPerDimm[Index] = -1; // If a dimm was not specified, filter it out here if (DimmIdsNum > 0 && !ContainUint(pDimmIds, DimmIdsNum, pDimms[Index].DimmID)) { continue; } // Initialize the successes of the specified dimm to 0 // Used to calculate if we return an error or not to CLI SuccessesPerDimm[Index] = 0; // For easier reading PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"\n"); // Collect logs from every debug log source on each dimm for (IndexSource = 0; IndexSource < NUM_FW_DEBUG_LOG_SOURCES; IndexSource++) { // Re-initialize pCommandStatus messages for each dimm and debug log source ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_DEVICE_ERROR; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto FreeAndContinue; } // Append dimm info, source, and .txt // We want to re-use pDumpUserPath, so use CatSPrint instead of // CatSPrintClean raw_file_name = CatSPrint(pDumpUserPath, L"_" FORMAT_STR L"_0x%04x_" FORMAT_STR L".bin", pDimms[Index].DimmUid, pDimms[Index].DimmHandle, SourceNames[IndexSource]); decoded_file_name = CatSPrint(pDumpUserPath, L"_" FORMAT_STR L"_0x%04x_" FORMAT_STR L".txt", pDimms[Index].DimmUid, pDimms[Index].DimmHandle, SourceNames[IndexSource]); if (raw_file_name == NULL || decoded_file_name == NULL) { goto FreeAndContinue; } ReturnCode = pNvmDimmConfigProtocol->GetFwDebugLog(pNvmDimmConfigProtocol, pDimms[Index].DimmID, IndexSource, Reserved, &RawLogBuffer, &RawLogBufferSizeBytes, pCommandStatus); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_NOT_STARTED) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"No " FORMAT_STR L" FW debug logs found\n", SourceNames[IndexSource]); goto FreeAndContinue; } if (pCommandStatus->GeneralStatus != NVM_SUCCESS) { ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Unexpected error in retrieving " FORMAT_STR L" FW debug logs\n", SourceNames[IndexSource]); PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, CLI_INFO_DUMP_DEBUG_LOG, L" ", pCommandStatus); goto FreeAndContinue; } } /** Get FW debug log **/ ReturnCode = DumpToFile(raw_file_name, RawLogBufferSizeBytes, RawLogBuffer, TRUE); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_VOLUME_FULL) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Not enough space to save file " FORMAT_STR L" with size %lu MiB\n", raw_file_name, BYTES_TO_MIB(RawLogBufferSizeBytes)); } else { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to dump " FORMAT_STR L" FW debug logs to file " FORMAT_STR L"\n", SourceNames[IndexSource], raw_file_name); } goto FreeAndContinue; } PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Dumped " FORMAT_STR L" FW debug logs to file " FORMAT_STR L"\n", SourceNames[IndexSource], raw_file_name); /** Decode FW debug log **/ if (dictExists) { decode_nlog_binary(pCmd, decoded_file_name, RawLogBuffer, RawLogBufferSizeBytes, dict_version, dict_head); } SuccessesPerDimm[Index]++; FreeAndContinue: FREE_POOL_SAFE(raw_file_name); FREE_POOL_SAFE(decoded_file_name); FREE_POOL_SAFE(RawLogBuffer); FreeCommandStatus(&pCommandStatus); } } // Return success if any of 3 logs were retrieved on every specified dimm ReturnCode = EFI_SUCCESS; for (Index = 0; Index < DimmCount; Index++) { if (SuccessesPerDimm[Index] == 0) { // If any specified dimm (initialized with 0) had no successes, then return error ReturnCode = EFI_DEVICE_ERROR; } } Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); while (dict_head) { next = dict_head->next; FREE_POOL_SAFE(dict_head->LogLevel); FREE_POOL_SAFE(dict_head->LogString); FREE_POOL_SAFE(dict_head->FileName); FREE_POOL_SAFE(dict_head); dict_head = next; } FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDictUserPath); FREE_POOL_SAFE(pDumpUserPath); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/DumpDebugCommand.h000066400000000000000000000014511440615110200212340ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DUMP_DEBUG_COMMAND_H_ #define _DUMP_DEBUG_COMMAND_H_ #include #include "NvmInterface.h" #include "Common.h" /** Register dump -debug command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterDumpDebugCommand( ); /** Dump FW debug log command @param[in] pCmd command from CLI @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS DumpDebugCommand( IN struct Command *pCmd ); #endif ipmctl-03.00.00.0485/DcpmPkg/cli/DumpGoalCommand.c000066400000000000000000000117471440615110200210740ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include "Debug.h" #include "Types.h" #include "Utility.h" #include "NvmInterface.h" #include "CommandParser.h" #include "DumpGoalCommand.h" #include "Common.h" /** Command syntax definition **/ struct Command DumpGoalCommand = { DUMP_VERB, //!< verb { //!< options {L"", DESTINATION_OPTION, L"", DESTINATION_OPTION_HELP, L"Destination to dump the goal ",FALSE, ValueRequired}, {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, { L"",PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, { L"",PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, { L"",LARGE_PAYLOAD_OPTION, L"", L"", HELP_LPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, { L"",SMALL_PAYLOAD_OPTION, L"", L"", HELP_SPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, { //!< targets {SYSTEM_TARGET, L"", L"", TRUE, ValueEmpty}, {CONFIG_TARGET, L"", L"", TRUE, ValueEmpty} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Store the current configured memory allocation settings to a file.", //!< help DumpGoal, TRUE, FALSE, FALSE, TRUE }; /** Execute the Dump Goal command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action @retval EFI_NO_RESPONSE FW busy for one or more dimms **/ EFI_STATUS DumpGoal( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; COMMAND_STATUS *pCommandStatus = NULL; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; CHAR16 *pDumpUserPath = NULL; CHAR16 *pDumpFilePath = NULL; EFI_DEVICE_PATH_PROTOCOL *pDevicePath = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; NVDIMM_ENTRY(); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; // NvmDimmConfigProtocol required ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } pDumpFilePath = AllocateZeroPool(OPTION_VALUE_LEN * sizeof(*pDumpFilePath)); if (pDumpFilePath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } // Check -destination option if (containsOption(pCmd, DESTINATION_OPTION)) { pDumpUserPath = getOptionValue(pCmd, DESTINATION_OPTION); if (pDumpUserPath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Could not get -destination value. Out of memory"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } } ReturnCode = GetDeviceAndFilePath(pDumpUserPath, pDumpFilePath, &pDevicePath); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get file path (" FORMAT_EFI_STATUS ")", ReturnCode); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_WRONG_FILE_PATH); goto Finish; } // Initialize status structure ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->DumpGoalConfig(pNvmDimmConfigProtocol, pDumpFilePath, pDevicePath, pCommandStatus); ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, CLI_DUMP_GOAL_MSG, CLI_DUMP_GOAL_ON_MSG, pCommandStatus); } else { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_INFO_DUMP_CONFIG_SUCCESS, pDumpUserPath); } Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pDumpFilePath); FREE_POOL_SAFE(pDumpUserPath); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the Dump Goal command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterDumpGoalCommand( ) { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&DumpGoalCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/DumpGoalCommand.h000066400000000000000000000015001440615110200210630ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DUMP_GOAL_COMMAND_ #define _DUMP_GOAL_COMMAND_ #include #define CLI_DUMP_GOAL_MSG L"Dump configuration goal" #define CLI_DUMP_GOAL_ON_MSG L" on" /** Register the Dump Goal command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterDumpGoalCommand(); /** Execute the Dump Goal command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS DumpGoal( IN struct Command *pCmd ); #endif /** _DUMP_GOAL_COMMAND_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/DumpSessionCommand.c000066400000000000000000000110421440615110200216210ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "Debug.h" #include "Types.h" #include "Utility.h" #include "NvmInterface.h" #include "CommandParser.h" #include "DumpSessionCommand.h" #include "Common.h" #include #ifdef OS_BUILD #include #endif #include #define SUCCESSFULLY_DUMPED_BUFFER_MSG L"Successfully dumped %d bytes to file." /** Command syntax definition **/ struct Command DumpSessionCommand = { DUMP_VERB, //!< verb { {L"", DESTINATION_OPTION, L"", DESTINATION_OPTION_HELP, L"Destination to dump the recorded session", FALSE, ValueRequired}, //!< options {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, { //!< targets {SESSION_TARGET, L"", L"", TRUE, ValueEmpty} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Dump the current recording (PBR) session buffer to a file.", //!< help DumpSession, TRUE }; /** Execute the Dump Session command **/ EFI_STATUS DumpSession( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol = NULL; CHAR16 *pDumpUserPath = NULL; CHAR16 *pDumpFilePath = NULL; UINT32 BufferSz = 0; VOID *pBuffer = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; PbrHeader *pHeader = NULL; NVDIMM_ENTRY(); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; // NvmDimmConfigProtocol required ReturnCode = OpenNvmDimmProtocol(gNvmDimmPbrProtocolGuid, (VOID **)&pNvmDimmPbrProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } //If Windows, check for admin privilege needed to update registry for PBR state CHECK_WIN_ADMIN_PERMISSIONS(); pDumpFilePath = AllocateZeroPool(OPTION_VALUE_LEN * sizeof(*pDumpFilePath)); if (pDumpFilePath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } pDumpUserPath = getOptionValue(pCmd, DESTINATION_OPTION); if (pDumpUserPath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Could not get -destination value. Out of memory"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } //get the contents of the pbr session buffer ReturnCode = pNvmDimmPbrProtocol->PbrGetSession(&pBuffer, &BufferSz); if (EFI_ERROR(ReturnCode) || pBuffer == NULL) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_GET_SESSION_BUFFER); goto Finish; } //fill in run-time versioning info pHeader = (PbrHeader*)pBuffer; #ifdef OS_BUILD os_get_os_name(pHeader->OsName, PBR_OS_NAME_MAX); os_get_os_version(pHeader->OsVersion, PBR_OS_VERSION_MAX); AsciiSPrint(pHeader->SwVersion, PBR_SW_VERSION_MAX, NVMDIMM_VERSION_STRING_A); #else AsciiSPrint(pHeader->OsName, PBR_OS_NAME_MAX, "UEFI"); UnicodeStrToAsciiStrS(NVMDIMM_VERSION_STRING, pHeader->SwVersion, PBR_SW_VERSION_MAX); #endif //dump the buffer to a file ReturnCode = DumpToFile(pDumpUserPath, BufferSz, pBuffer, TRUE); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_DUMP_SESSION_TO_FILE); goto Finish; } PRINTER_SET_MSG(pPrinterCtx, ReturnCode, SUCCESSFULLY_DUMPED_BUFFER_MSG, BufferSz); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pBuffer); FREE_POOL_SAFE(pDumpFilePath); FREE_POOL_SAFE(pDumpUserPath); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the Dump PBR command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterDumpSessionCommand( ) { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&DumpSessionCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/DumpSessionCommand.h000066400000000000000000000013441440615110200216320ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DUMP_SESSION_COMMAND_ #define _DUMP_SESSION_COMMAND_ #include /** Register the Dump Session command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterDumpSessionCommand(); /** Execute the Dump Session command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS DumpSession( IN struct Command *pCmd ); #endif /** _DUMP_SESSION_COMMAND_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/LoadCommand.c000066400000000000000000000571671440615110200202510ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include "LoadCommand.h" #include #include #include #include #include #include #include "NvmDimmCli.h" #include "Common.h" /** Command syntax definition **/ struct Command LoadCommand = { LOAD_VERB, //!< verb { //!< options {L"", SOURCE_OPTION, L"", SOURCE_OPTION_HELP, L"Firmware Image required to use Firmware Update.", FALSE, ValueRequired}, {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"", HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", LARGE_PAYLOAD_OPTION, L"", L"", HELP_LPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", SMALL_PAYLOAD_OPTION, L"", L"", HELP_SPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, {EXAMINE_OPTION_SHORT, EXAMINE_OPTION, L"", L"", EXAMINE_OPTION_DETAILS_TEXT, FALSE, ValueEmpty}, {FORCE_OPTION_SHORT, FORCE_OPTION, L"", L"", FORCE_OPTION_DETAILS_TEXT, FALSE, ValueEmpty}, { L"", RECOVER_OPTION, L"", L"", RECOVER_OPTION_DETAILS_TEXT, FALSE, ValueEmpty } #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, { //!< targets {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, TRUE, ValueOptional} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Update the firmware on one or more " PMEM_MODULES_STR L".", //!< help Load, //!< run function TRUE //!< Enable Printer }; /** Register the load command **/ EFI_STATUS RegisterLoadCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&LoadCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Execute the load command @param[in] pCmd the command structure that contains the user input data. @retval EFI_SUCCESS if everything went OK - including the firmware load process. @retval EFI_INVALID_PARAMETER if the user input is invalid or the file validation fails @retval EFI_UNSUPPORTED if the driver is not loaded or there are no DCPMMs in the system. @retval EFI_NOT_FOUND if there is no DIMM with the user specified PID @retval EFI_NO_RESPONSE FW busy for one or more dimms **/ EFI_STATUS Load( IN struct Command *pCmd ) { BOOLEAN fileExists = FALSE; EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TempReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; CHAR16 *pFileName = NULL; CHAR16 *pRelativeFileName = NULL; CHAR16 *pTargetValue = NULL; CONST CHAR16 *pWorkingDirectory = NULL; UINT32 DimmHandle = 0; UINT32 DimmIndex = 0; COMMAND_STATUS *pCommandStatus = NULL; BOOLEAN Examine = FALSE; BOOLEAN Force = FALSE; NVM_FW_IMAGE_INFO *pFwImageInfo = NULL; volatile UINT32 Index = 0; volatile UINT32 Index2 = 0; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; EFI_EVENT ProgressEvent = NULL; CHAR16 *pOptionsValue = NULL; BOOLEAN Recovery = FALSE; DIMM_INFO *pDimmTargets = NULL; UINT16 *pDimmIds = NULL; UINT16 *pDimmTargetIds = NULL; UINT32 StagedFwUpdates = 0; DIMM_INFO *pDimms = NULL; DIMM_INFO *pCandidateList = NULL; UINT32 DimmCount = 0; UINT32 DimmTargetsNum = 0; UINT32 CandidateListCount = 0; BOOLEAN TargetsIsNewList = FALSE; BOOLEAN Confirmation = 0; EFI_STATUS ReturnCodes[MAX_DIMMS]; NVM_STATUS NvmCodes[MAX_DIMMS]; NVM_STATUS generalNvmStatus = NVM_SUCCESS; #ifndef OS_BUILD EFI_SHELL_PROTOCOL *pEfiShell = NULL; UINTN HandlesCount = 0; EFI_HANDLE *pHandles = NULL; #endif if ((NULL != pCmd) && (NULL != pCmd->pPrintCtx)) { if (pCmd->pPrintCtx->FormatType == XML) { PRINTER_CONFIGURE_BUFFERING(pCmd->pPrintCtx, ON); } else { PRINTER_CONFIGURE_BUFFERING(pCmd->pPrintCtx, OFF); } } NVDIMM_ENTRY(); SetDisplayInfo(L"LoadFw", ResultsView, NULL); for (Index = 0; Index < MAX_DIMMS; Index++) { ReturnCodes[Index] = EFI_SUCCESS; NvmCodes[Index] = NVM_SUCCESS; } ZeroMem(DimmStr, sizeof(DimmStr)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PrinterSetMsg(NULL, ReturnCode, CLI_ERR_NO_COMMAND); goto FinishNoCommandStatus; } // initialize status structure ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { PrinterSetMsg(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); // if pCMD->pPrintCtx is NULL then will print to stdout NVDIMM_DBG("Failed on InitializeCommandStatus"); goto FinishNoCommandStatus; } ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PrinterSetMsg(pCmd->pPrintCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // check options pFileName = getOptionValue(pCmd, SOURCE_OPTION); if (pFileName == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PrinterSetMsg(pCmd->pPrintCtx, ReturnCode, CLI_ERR_WRONG_FILE_PATH); goto Finish; } if (containsOption(pCmd, EXAMINE_OPTION) && containsOption(pCmd, EXAMINE_OPTION_SHORT)) { ReturnCode = EFI_INVALID_PARAMETER; PrinterSetMsg(pCmd->pPrintCtx, ReturnCode, CLI_ERR_OPTIONS_EXAMINE_USED_TOGETHER); goto Finish; } if (containsOption(pCmd, FORCE_OPTION) && containsOption(pCmd, FORCE_OPTION_SHORT)) { ReturnCode = EFI_INVALID_PARAMETER; PrinterSetMsg(pCmd->pPrintCtx, ReturnCode, CLI_ERR_OPTIONS_FORCE_USED_TOGETHER); goto Finish; } Recovery = containsOption(pCmd, RECOVER_OPTION); Examine = containsOption(pCmd, EXAMINE_OPTION) || containsOption(pCmd, EXAMINE_OPTION_SHORT); Force = containsOption(pCmd, FORCE_OPTION) || containsOption(pCmd, FORCE_OPTION_SHORT); /*Get the list of functional and non-functional dimms*/ CHECK_RESULT(GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_SMART_AND_HEALTH, &pDimms, &DimmCount), Finish); if (DimmCount == 0) { ReturnCode = EFI_NOT_STARTED; PrinterSetMsg(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_DIMMS); goto Finish; } // Include all DCPMMs for fw update / spi flash update pCandidateList = pDimms; CandidateListCount = DimmCount; /*Screen for user specific IDs*/ pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); if (pTargetValue != NULL && StrLen(pTargetValue) > 0) { ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pCandidateList, CandidateListCount, &pDimmIds, &DimmTargetsNum); if (pDimmIds == NULL) { goto Finish; } if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetDimmIdsFromString"); goto Finish; } TargetsIsNewList = TRUE; pDimmTargets = AllocateZeroPool(sizeof(*pDimmTargets) * DimmTargetsNum); pDimmTargetIds = AllocateZeroPool(sizeof(*pDimmTargetIds) * DimmTargetsNum); if (pDimmTargets == NULL || pDimmTargetIds == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PrinterSetMsg(pCmd->pPrintCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } for (Index = 0; Index < DimmTargetsNum; Index++) { for (Index2 = 0; Index2 < CandidateListCount; Index2++) { if (pCandidateList[Index2].DimmID == pDimmIds[Index]) { pDimmTargets[Index] = pCandidateList[Index2]; pDimmTargetIds[Index] = pCandidateList[Index2].DimmID; break; } } } } else { DimmTargetsNum = CandidateListCount; pDimmTargets = pCandidateList; pDimmTargetIds = AllocateZeroPool(sizeof(*pDimmTargetIds) * DimmTargetsNum); if (pDimmTargetIds == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PrinterSetMsg(pCmd->pPrintCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } for (Index = 0; Index < DimmTargetsNum; Index++) { pDimmTargetIds[Index] = pDimmTargets[Index].DimmID; } } if (NULL == pDimmTargets) { ReturnCode = EFI_NOT_FOUND; CHECK_RETURN_CODE(ReturnCode, Finish); } /** In this case the user could have typed "FS0:\..." We are searching for the file on all FS so we need to remove the first chars until we have a "\" **/ #ifdef OS_BUILD pRelativeFileName = pFileName; #else ReturnCode = GetRelativePath(pFileName, &pRelativeFileName); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = gBS->LocateHandleBuffer(ByProtocol, &gEfiShellProtocolGuid, NULL, &HandlesCount, &pHandles); if (!EFI_ERROR(ReturnCode) && HandlesCount < MAX_SHELL_PROTOCOL_HANDLES) { ReturnCode = gBS->OpenProtocol(pHandles[0], &gEfiShellProtocolGuid, (VOID *)&pEfiShell, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (!EFI_ERROR(ReturnCode)) { pWorkingDirectory = pEfiShell->GetCurDir(NULL); if (pWorkingDirectory == NULL) { NVDIMM_WARN("Error while getting the Working Directory."); } } else { NVDIMM_WARN("Error while opening the shell protocol. Code: " FORMAT_EFI_STATUS "", ReturnCode); } } else { NVDIMM_WARN("Error while opening the shell protocol. Code: " FORMAT_EFI_STATUS "", ReturnCode); /** We can still try to open the file. If it is in the root directory, we will be able to open it. **/ } #endif ReturnCode = FileExists(pFileName, &fileExists); if (EFI_ERROR(ReturnCode) || FALSE == fileExists) { NVDIMM_DBG("OpenFile returned: " FORMAT_EFI_STATUS ".\n", ReturnCode); ResetCmdStatus(pCommandStatus, NVM_ERR_FILE_NOT_FOUND); ReturnCode = MatchCliReturnCode(NVM_ERR_FILE_NOT_FOUND); goto Finish; } pFwImageInfo = AllocateZeroPool(sizeof(*pFwImageInfo)); if (pFwImageInfo == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PrinterSetMsg(pCmd->pPrintCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_NOT_STARTED); if (!Examine) { PrinterSetMsg(pCmd->pPrintCtx, EFI_SUCCESS, L"Starting update on %d " PMEM_MODULE_STR L"(s)...", DimmTargetsNum); // Create callback that will print progress gBS->CreateEvent((EVT_TIMER | EVT_NOTIFY_SIGNAL), PRINT_PRIORITY, PrintProgress, pCommandStatus, &ProgressEvent); gBS->SetTimer(ProgressEvent, TimerPeriodic, PROGRESS_EVENT_TIMEOUT); } for (Index = 0; Index < DimmTargetsNum; Index++) { pCommandStatus->GeneralStatus = NVM_SUCCESS; //ensure that only the last error gets reported //if the FW is already staged and this isn't an examine operation, the outcome is already known if (FALSE == Examine && TRUE == FwHasBeenStaged(pCmd, pNvmDimmConfigProtocol, pDimmTargets[Index].DimmID)) { pCommandStatus->GeneralStatus = NVM_ERR_FIRMWARE_ALREADY_LOADED; NvmCodes[Index] = pCommandStatus->GeneralStatus; ReturnCodes[Index] = MatchCliReturnCode(NvmCodes[Index]); SetObjStatusForDimmInfoWithErase(pCommandStatus, &pDimmTargets[Index], NvmCodes[Index], TRUE); continue; } ReturnCodes[Index] = pNvmDimmConfigProtocol->UpdateFw(pNvmDimmConfigProtocol, &pDimmTargetIds[Index], 1, pRelativeFileName, (CHAR16 *)pWorkingDirectory, Examine, Force, Recovery, FALSE, pFwImageInfo, pCommandStatus); NvmCodes[Index] = pCommandStatus->GeneralStatus; if (Examine) { if (NvmCodes[Index] == NVM_ERR_FIRMWARE_TOO_LOW_FORCE_REQUIRED) { ReturnCodes[Index] = EFI_SUCCESS; NvmCodes[Index] = NVM_SUCCESS; } continue; } if (pCommandStatus->GeneralStatus == NVM_ERR_FIRMWARE_TOO_LOW_FORCE_REQUIRED) { ReturnCodes[Index] = GetDimmHandleByPid(pDimmTargetIds[Index], pDimmTargets, DimmTargetsNum, &DimmHandle, &DimmIndex); if (EFI_ERROR(ReturnCodes[Index])) { NVDIMM_DBG("Failed to get dimm handle"); NvmCodes[Index] = NVM_ERR_DIMM_NOT_FOUND; goto Finish; } ReturnCodes[Index] = GetPreferredDimmIdAsString(DimmHandle, pDimmTargets[DimmIndex].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCodes[Index])) { NvmCodes[Index] = NVM_ERR_INVALID_PARAMETER; SetObjStatusForDimmInfoWithErase(pCommandStatus, &pDimmTargets[Index], NvmCodes[Index], TRUE); goto Finish; } NVDIMM_BUFFER_CONTROLLED_MSG(FALSE, CLI_DOWNGRADE_PROMPT L"\n", DimmStr); ReturnCodes[Index] = PromptYesNo(&Confirmation); if (EFI_ERROR(ReturnCodes[Index]) || !Confirmation) { NvmCodes[Index] = NVM_ERR_FIRMWARE_TOO_LOW_FORCE_REQUIRED; ReturnCodes[Index] = EFI_ABORTED; SetObjStatusForDimmInfoWithErase(pCommandStatus, &pDimmTargets[Index], NvmCodes[Index], TRUE); continue; } ReturnCodes[Index] = pNvmDimmConfigProtocol->UpdateFw(pNvmDimmConfigProtocol, &pDimmTargetIds[Index], 1, pRelativeFileName, (CHAR16 *)pWorkingDirectory, Examine, TRUE, Recovery, FALSE, pFwImageInfo, pCommandStatus); if (EFI_ERROR(ReturnCodes[Index])) { continue; } } else if (EFI_ERROR(ReturnCodes[Index])) { continue; } StagedFwUpdates++; } //for loop if (Examine) { //only print non 0.0.0.0 versions... if (pFwImageInfo->ImageVersion.ProductNumber.Version != 0 || pFwImageInfo->ImageVersion.RevisionNumber.Version != 0 || pFwImageInfo->ImageVersion.SecurityRevisionNumber.Version != 0 || pFwImageInfo->ImageVersion.BuildNumber.Build != 0) { PrinterSetMsg(pCmd->pPrintCtx, ReturnCode, FORMAT_STR L": %02d.%02d.%02d.%04d", pFileName, pFwImageInfo->ImageVersion.ProductNumber.Version, pFwImageInfo->ImageVersion.RevisionNumber.Version, pFwImageInfo->ImageVersion.SecurityRevisionNumber.Version, pFwImageInfo->ImageVersion.BuildNumber.Build); } } else { gBS->CloseEvent(ProgressEvent); // move to next line after progress events have ended PrinterSetMsg(pCmd->pPrintCtx, ReturnCode, FORMAT_NL); if (StagedFwUpdates > 0) { /* At this point, all indications are that the FW is on the way to being staged. Loop until they all report a staged version */ TempReturnCode = BlockForFwStage(pCmd, pCommandStatus, pNvmDimmConfigProtocol, &ReturnCodes[0], &NvmCodes[0], pDimmTargets, DimmTargetsNum); if (EFI_ERROR(TempReturnCode)) { ReturnCode = TempReturnCode; goto Finish; } } } ReturnCode = EFI_SUCCESS; pCommandStatus->GeneralStatus = NVM_SUCCESS; for (Index = 0; Index < DimmTargetsNum; Index++) { TempReturnCode = GetDimmReturnCode(Examine, ReturnCodes[Index], NvmCodes[Index], &generalNvmStatus); //the 'EFI_ALREADY_STARTED' return code is considered a minor success //so if another error is present then prefer to report that error instead //(in other words, if 'ReturnCode' has been set to something other than 'EFI_ALREADY_STARTED' or 'EFI_SUCCESS' // then don't let it be altered) if (TempReturnCode == EFI_SUCCESS || (TempReturnCode == EFI_ALREADY_STARTED && ReturnCode != EFI_SUCCESS && ReturnCode != TempReturnCode)) { continue; } ReturnCode = TempReturnCode; pCommandStatus->GeneralStatus = generalNvmStatus; } Finish: PRINTER_SET_COMMAND_STATUS(pCmd->pPrintCtx, ReturnCode, CLI_INFO_LOAD_FW, CLI_INFO_ON, pCommandStatus); FreeCommandStatus(&pCommandStatus); FinishNoCommandStatus: // if no PrintCtx then nothing can be buffered so no need to process it if ((NULL != pCmd)) { PRINTER_PROCESS_SET_BUFFER(pCmd->pPrintCtx); } FREE_POOL_SAFE(pFileName); FREE_POOL_SAFE(pFwImageInfo); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pOptionsValue); FREE_POOL_SAFE(pDimmTargetIds); FREE_POOL_SAFE(pDimms); if (TargetsIsNewList) { FREE_POOL_SAFE(pDimmTargets); } NVDIMM_EXIT_I64(ReturnCode); FREE_POOL_SAFE(pOptionsValue); return ReturnCode; } /** For a given DIMM, this will evaluate what the return code should be @param[in] examine - if the examin flag was sent by the user @param[in] dimmReturnCode - the return code returned by the call to update the FW @param[in] dimmNvmStatus - the NVM status returned by the call to update the FW @param[out] generalNvmStatus - the NVM status to be applied to the general command status @retval the return code **/ EFI_STATUS GetDimmReturnCode( IN BOOLEAN Examine, IN EFI_STATUS dimmReturnCode, IN NVM_STATUS dimmNvmStatus, OUT NVM_STATUS * pGeneralNvmStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_STATUS NvmStatus = NVM_SUCCESS; NVDIMM_ENTRY(); *pGeneralNvmStatus = NVM_SUCCESS; if (Examine && (dimmNvmStatus == NVM_ERR_FIRMWARE_TOO_LOW_FORCE_REQUIRED || dimmNvmStatus == NVM_SUCCESS)) { //these are both considered success when in examine goto Finish; } *pGeneralNvmStatus = dimmNvmStatus; NvmStatus = dimmNvmStatus; Finish: ReturnCode = MatchCliReturnCode(NvmStatus); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** For the lists provided, this will block until all dimms indicated in the StagedFwDimmIds report a non-zero staged FW image. This is intended to be run after a non-recovery (normal) FW update. @param[in] pCmd - The command object @param[in] pCommandStatus - The command status object @param[in] pNvmDimmConfigProtocol - The open config protocol @param[in] pReturnCodes - The current list of return codes for each DIMM @param[in] pNvmCodes - The current list of NVM codes for the FW work of each DIMM @param[in] pDimmTargets - The list of DIMMs for which a FW update was attempted @param[in] pDimmTargetsNum - The list length of the pDimmTargets list @retval EFI_SUCCESS - All dimms staged their fw as expected. @retval EFI_xxxx - One or more DIMMS did not stage their FW as expected. **/ EFI_STATUS BlockForFwStage( IN struct Command *pCmd, IN COMMAND_STATUS *pCommandStatus, IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN EFI_STATUS *pReturnCodes, IN NVM_STATUS *pNvmCodes, IN DIMM_INFO *pDimmTargets, IN UINT32 pDimmTargetsNum ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 CurrentStageCheck = 0; EFI_STATUS FwStagedLongOpCodes[MAX_DIMMS]; BOOLEAN FwStageDone[MAX_DIMMS]; UINT16 FwStagedPendingCount = 0; UINT16 FwStagedCompleteCount = 0; UINT8 CmdOpcode = 0; UINT8 CmdSubOpcode = 0; UINT8 PtUpdateFw = 0x09; UINT8 SubopUpdateFw = 0x0; EFI_STATUS LongOpEfiStatus = EFI_SUCCESS; UINT8 TransmitFwNeverHappened = 0xFF; UINT8 UnknownStatus = 0xFD; volatile UINT32 Index = 0; NVDIMM_ENTRY(); if (pDimmTargetsNum > MAX_DIMMS) { NVDIMM_DBG("Number of target DIMMs is greater than max DIMMs number."); goto Finish; } //initialize for (Index = 0; Index < MAX_DIMMS; Index++) { FwStagedLongOpCodes[Index] = UnknownStatus; FwStageDone[Index] = FALSE; } //more initialize for (Index = 0; Index < pDimmTargetsNum; Index++) { if (pReturnCodes[Index] != EFI_SUCCESS) { //this DIMM didn't transmit an image. Don't expect a long op code FwStagedLongOpCodes[Index] = TransmitFwNeverHappened; FwStagedCompleteCount++; FwStageDone[Index] = TRUE; NVDIMM_DBG("Error with FW stage on dimm %d: an existing error exists - NvmCode=[%d], ReturnCode=[%d]", pDimmTargets[Index].DimmID, pNvmCodes[Index], pReturnCodes[Index]); continue; } FwStagedPendingCount++; CmdOpcode = 0; CmdSubOpcode = 0; pNvmDimmConfigProtocol->GetLongOpStatus(pNvmDimmConfigProtocol, pDimmTargets[Index].DimmID, &CmdOpcode, &CmdSubOpcode, NULL, NULL, &LongOpEfiStatus); if (CmdOpcode == PtUpdateFw && CmdSubOpcode == SubopUpdateFw) { FwStagedLongOpCodes[Index] = LongOpEfiStatus; } } if (FwStagedPendingCount == 0) { NVDIMM_DBG("No DIMMs have images expected to stage. Exiting."); goto Finish; } while (CurrentStageCheck < MAX_CHECKS_FOR_SUCCESSFUL_STAGING) { CurrentStageCheck++; //Check each DIMM that had a image staged to see if it reports a staged version for (Index = 0; Index < pDimmTargetsNum; Index++) { //don't perform unnecessary checks or repeat checks that already took place if (FwStagedLongOpCodes[Index] == TransmitFwNeverHappened || FwStageDone[Index] == TRUE) { continue; } if (FwHasBeenStaged(pCmd, pNvmDimmConfigProtocol, pDimmTargets[Index].DimmID) == TRUE) { pNvmCodes[Index] = NVM_SUCCESS_FW_RESET_REQUIRED; pReturnCodes[Index] = EFI_SUCCESS; SetObjStatusForDimmInfoWithErase(pCommandStatus, &pDimmTargets[Index], pNvmCodes[Index], TRUE); FwStagedCompleteCount++; FwStageDone[Index] = TRUE; FwStagedLongOpCodes[Index] = FW_SUCCESS; NVDIMM_DBG("FW stage detected for dimm %d", pDimmTargets[Index].DimmID); continue; } CmdOpcode = 0; CmdSubOpcode = 0; pNvmDimmConfigProtocol->GetLongOpStatus(pNvmDimmConfigProtocol, pDimmTargets[Index].DimmID, &CmdOpcode, &CmdSubOpcode, NULL, NULL, &LongOpEfiStatus); if (CmdOpcode == PtUpdateFw && CmdSubOpcode == SubopUpdateFw) { if (LongOpEfiStatus != EFI_NO_RESPONSE && LongOpEfiStatus != EFI_SUCCESS) { NVDIMM_DBG("Error with FW stage on dimm %d: Long operation failed - LongOpEfiStatus=[%d]", pDimmTargets[Index].DimmID, LongOpEfiStatus); if (LongOpEfiStatus == EFI_DEVICE_ERROR) { pNvmCodes[Index] = NVM_ERR_DEVICE_ERROR; } else if (LongOpEfiStatus == EFI_UNSUPPORTED) { pNvmCodes[Index] = NVM_ERR_UNSUPPORTED_COMMAND; } else if (LongOpEfiStatus == EFI_SECURITY_VIOLATION) { pNvmCodes[Index] = NVM_ERR_FW_UPDATE_AUTH_FAILURE; } else if (LongOpEfiStatus == EFI_ABORTED) { pNvmCodes[Index] = NVM_ERR_LONG_OP_ABORTED_OR_REVISION_FAILURE; } else { pNvmCodes[Index] = NVM_ERR_LONG_OP_UNKNOWN; } pReturnCodes[Index] = LongOpEfiStatus; SetObjStatusForDimmInfoWithErase(pCommandStatus, &pDimmTargets[Index], pNvmCodes[Index], TRUE); FwStagedCompleteCount++; FwStageDone[Index] = TRUE; FwStagedLongOpCodes[Index] = LongOpEfiStatus; } } } if (FwStagedCompleteCount == pDimmTargetsNum) { break; //while loop } gBS->Stall(MICROSECONDS_PERIOD_BETWEEN_STAGING_CHECKS); } //mark any DIMM which was pending an update but never confirmed it as a failure for (Index = 0; Index < pDimmTargetsNum; Index++) { if (FwStageDone[Index] == FALSE) { NVDIMM_DBG("Error with FW stage on dimm %d: Long operation status unknown, image never staged", pDimmTargets[Index].DimmID); pNvmCodes[Index] = NVM_ERR_UNABLE_TO_STAGE_NO_LONGOP; pReturnCodes[Index] = EFI_ABORTED; SetObjStatusForDimmInfoWithErase(pCommandStatus, &pDimmTargets[Index], pNvmCodes[Index], TRUE); ReturnCode = EFI_ABORTED; } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check to see if a FW has already been staged on a DIMM @param[in] pCmd - The command object @param[in] pNvmDimmConfigProtocol - The open config protocol @param[in] DimmID - The ID of the dimm to check. Must be a functional DIMM **/ BOOLEAN FwHasBeenStaged( IN struct Command *pCmd, IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN UINT16 DimmID ) { BOOLEAN RetBool = FALSE; EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM_INFO Dimm; NVDIMM_ENTRY(); CHECK_RESULT((pNvmDimmConfigProtocol->GetDimm(pNvmDimmConfigProtocol, DimmID, DIMM_INFO_CATEGORY_FW_IMAGE_INFO, &Dimm)), Finish); if (FALSE == FW_VERSION_UNDEFINED(Dimm.StagedFwVersion)) { RetBool = TRUE; } Finish: NVDIMM_EXIT_I64(ReturnCode); return RetBool; } ipmctl-03.00.00.0485/DcpmPkg/cli/LoadCommand.h000066400000000000000000000060521440615110200202410ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _LOAD_COMMAND_H_ #define _LOAD_COMMAND_H_ #include #include "CommandParser.h" #include #include #define STAGING_CHECKS_PER_SECOND 4 #define MAX_CHECKS_FOR_SUCCESSFUL_STAGING (FW_UPDATE_TIMEOUT_SECONDS)*STAGING_CHECKS_PER_SECOND #define MICROSECONDS_PERIOD_BETWEEN_STAGING_CHECKS 1000000/STAGING_CHECKS_PER_SECOND /** Register the load command **/ EFI_STATUS RegisterLoadCommand ( ); /** Execute the load command @param[in] pCmd the command structure that contains the user input data. @retval EFI_SUCCESS if everything went OK - including the firmware load process. @retval EFI_INVALID_PARAMETER if the user input is invalid or the file validation fails @retval EFI_UNSUPPORTED if the driver is not loaded or there are no DCPMMs in the system. @retval EFI_NOT_FOUND if there is no DIMM with the user specified PID **/ EFI_STATUS Load( IN struct Command *pCmd ); /** For a given DIMM, this will evaluate what the return code should be @param[in] examine - if the examine flag was sent by the user @param[in] dimmReturnCode - the return code returned by the call to update the FW @param[in] dimmNvmStatus - the NVM status returned by the call to update the FW @param[out] generalNvmStatus - the NVM status to be applied to the general command status @retval the return code **/ EFI_STATUS GetDimmReturnCode( IN BOOLEAN examine, IN EFI_STATUS dimmReturnCode, IN NVM_STATUS dimmNvmStatus, OUT NVM_STATUS * pGeneralNvmStatus ); /** For the lists provided, this will block until all dimms indicated in the StagedFwDimmIds report a non-zero staged FW image. This is intended to be run after a non-recovery (normal) FW update. @param[in] pCmd - The command object @param[in] pCommandStatus - The command status object @param[in] pNvmDimmConfigProtocol - The open config protocol @param[in] pReturnCodes - The current list of return codes for each DIMM @param[in] pNvmCodes - The current list of NVM codes for the FW work of each DIMM @param[in] pDimmTargets - The list of DIMMs for which a FW update was attempted @param[in] pDimmTargetsNum - The list length of the pDimmTargets list @retval EFI_SUCCESS - All dimms staged their fw as expected. @retval EFI_xxxx - One or more DIMMS did not stage their FW as expected. **/ EFI_STATUS BlockForFwStage( IN struct Command *pCmd, IN COMMAND_STATUS *pCommandStatus, IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN EFI_STATUS *pReturnCodes, IN NVM_STATUS *pNvmCodes, IN DIMM_INFO *pDimmTargets, IN UINT32 pDimmTargetsNum ); /** Check to see if a FW has already been staged on a DIMM @param[in] pCmd - The command object @param[in] pNvmDimmConfigProtocol - The open config protocol @param[in] DimmID - The ID of the dimm to check. Must be a functional DIMM **/ BOOLEAN FwHasBeenStaged( IN struct Command *pCmd, IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN UINT16 DimmID ); #endif /** _LOAD_COMMAND_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/LoadGoalCommand.c000066400000000000000000000333671440615110200210500ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "Debug.h" #include "Types.h" #include "Utility.h" #include "NvmInterface.h" #include "CommandParser.h" #include "LoadGoalCommand.h" #include "CreateGoalCommand.h" #include "Common.h" #include "NvmDimmCli.h" #include /** Command syntax definition **/ struct Command LoadGoalCommand = { LOAD_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"", HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", LARGE_PAYLOAD_OPTION, L"", L"", HELP_LPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", SMALL_PAYLOAD_OPTION, L"", L"", HELP_SPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, {FORCE_OPTION_SHORT, FORCE_OPTION, L"", L"", HELP_FORCE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", SOURCE_OPTION, L"", SOURCE_OPTION_HELP, L"Source Directory required to load goal", FALSE, ValueRequired}, {UNITS_OPTION_SHORT, UNITS_OPTION, L"", UNITS_OPTION_HELP, HELP_UNIT_DETAILS_TEXT, FALSE, ValueRequired} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, { //!< targets {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional}, {GOAL_TARGET, L"", L"", TRUE, ValueEmpty}, {SOCKET_TARGET, L"", HELP_TEXT_SOCKET_IDS, FALSE, ValueOptional} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Create a memory allocation goal request from a file.", //!< help LoadGoal, TRUE }; /** Execute the Load Goal command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action @retval EFI_NO_RESPONSE FW busy on one or more dimms **/ EFI_STATUS LoadGoal( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT16 *pDimmIds = NULL; UINT32 DimmIdsCount = 0; UINT16 *pSocketIds = NULL; UINT32 SocketIdsCount = 0; COMMAND_STATUS *pCommandStatus = NULL; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; CHAR16 *pLoadUserPath = NULL; CHAR16 *pLoadFilePath = NULL; CHAR16 *pTargetValue = NULL; CHAR8 *pFileString = NULL; CHAR16 *pCommandStr = NULL; EFI_DEVICE_PATH_PROTOCOL *pDevicePathProtocol = NULL; BOOLEAN Force = FALSE; COMMAND_INPUT ShowGoalCmdInput; COMMAND ShowGoalCmd; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; UINT16 UnitsOption = DISPLAY_SIZE_UNIT_UNKNOWN; UINT16 UnitsToDisplay = FixedPcdGet16(PcdDcpmmCliDefaultCapacityUnit); CHAR16 *pUnitsStr = NULL; DISPLAY_PREFERENCES DisplayPreferences; UINT32 SocketIndex = 0; BOOLEAN Confirmation = FALSE; INTEL_DIMM_CONFIG *pIntelDIMMConfig = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 * pShowGoalOutputArgs = NULL; BOOLEAN isDimmUnlocked = FALSE; NVDIMM_ENTRY(); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); ZeroMem(&ShowGoalCmdInput, sizeof(ShowGoalCmdInput)); ZeroMem(&ShowGoalCmd, sizeof(ShowGoalCmd)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; Print(FORMAT_STR_NL, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; // NvmDimmConfigProtocol required ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_SECURITY, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } UnitsToDisplay = DisplayPreferences.SizeUnit; ReturnCode = GetUnitsOption(pCmd, &UnitsOption); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Any valid units option will override the preferences **/ if (UnitsOption != DISPLAY_SIZE_UNIT_UNKNOWN) { UnitsToDisplay = UnitsOption; } if (containsOption(pCmd, FORCE_OPTION) || containsOption(pCmd, FORCE_OPTION_SHORT) || XML == pPrinterCtx->FormatType) { Force = TRUE; } pLoadFilePath = AllocateZeroPool(OPTION_VALUE_LEN * sizeof(*pLoadFilePath)); if (pLoadFilePath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } // Check -source option if (containsOption(pCmd, SOURCE_OPTION)) { pLoadUserPath = getOptionValue(pCmd, SOURCE_OPTION); if (pLoadUserPath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Could not get -source value. Out of memory"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } } ReturnCode = GetDeviceAndFilePath(pLoadUserPath, pLoadFilePath, &pDevicePathProtocol); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get file path (" FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } // check if auto provision is enabled RetrieveIntelDIMMConfig(&pIntelDIMMConfig); if(pIntelDIMMConfig != NULL) { if (pIntelDIMMConfig->ProvisionCapacityMode == PROVISION_CAPACITY_MODE_AUTO) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_CREATE_GOAL_AUTO_PROV_ENABLED); FreePool(pIntelDIMMConfig); goto Finish; } else { FreePool(pIntelDIMMConfig); } } // Check targets if (ContainTarget(pCmd, DIMM_TARGET)) { pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetDimmIdsFromString"); goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsCount)){ ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } if (!AllDimmsInListInSupportedConfig(pDimms, DimmCount, pDimmIds, DimmIdsCount)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_POPULATION_VIOLATION); goto Finish; } } if (ContainTarget(pCmd, SOCKET_TARGET)) { pTargetValue = GetTargetValue(pCmd, SOCKET_TARGET); ReturnCode = GetUintsFromString(pTargetValue, &pSocketIds, &SocketIdsCount); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_TARGET_SOCKET); NVDIMM_DBG("Failed on GetUintsFromString"); goto Finish; } } // Initialize status structure ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } ReturnCode = ParseSourceDumpFile(pLoadFilePath, pDevicePathProtocol, &pFileString); if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_LOAD_INVALID_DATA_IN_FILE); ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_COMMAND_STATUS(pCmd->pPrintCtx, ReturnCode, CLI_INFO_LOAD_GOAL, L"", pCommandStatus); goto Finish; } if (!Force) { PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, CLI_INFO_LOAD_GOAL_CONFIRM_PROMPT, pLoadFilePath); ReturnCode = AreRequestedDimmsSecurityUnlocked(pDimms, DimmCount, pDimmIds, DimmIdsCount, &isDimmUnlocked); if (EFI_ERROR(ReturnCode)) { goto Finish; } // send warning if security unlocked for target dimms if (isDimmUnlocked) { PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, CLI_WARN_GOAL_CREATION_SECURITY_UNLOCKED); } ReturnCode = PromptYesNo(&Confirmation); if (EFI_ERROR(ReturnCode)) { PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_PROMPT_INVALID); NVDIMM_DBG("Failed on PromptedInput"); goto Finish; } else if (!Confirmation) { goto Finish; } } ReturnCode = pNvmDimmConfigProtocol->LoadGoalConfig(pNvmDimmConfigProtocol, pDimmIds, DimmIdsCount, pSocketIds, SocketIdsCount, pFileString, pCommandStatus); if (EFI_ERROR(ReturnCode)) { if (pCommandStatus->GeneralStatus != NVM_SUCCESS) { ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_COMMAND_STATUS(pCmd->pPrintCtx, ReturnCode, CLI_INFO_LOAD_GOAL, L"", pCommandStatus); goto Finish; } } if (!EFI_ERROR(ReturnCode)) { ReturnCode = CreateCmdLineOutputStr(pCmd, &pShowGoalOutputArgs); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } if (UnitsOption != DISPLAY_SIZE_UNIT_UNKNOWN) { CHECK_RESULT(UnitsToStr(gNvmDimmCliHiiHandle, UnitsToDisplay, &pUnitsStr), Finish); pCommandStr = CatSPrintClean(pCommandStr, FORMAT_STR_SPACE FORMAT_STR FORMAT_STR L" " FORMAT_STR L" " FORMAT_STR, L"show", pShowGoalOutputArgs, UNITS_OPTION, pUnitsStr, L"-goal"); } else { pCommandStr = CatSPrintClean(pCommandStr, FORMAT_STR_SPACE FORMAT_STR FORMAT_STR, L"show", pShowGoalOutputArgs, L"-goal"); } // Only print the socket applied if (SocketIdsCount > 0) { pCommandStr = CatSPrintClean(pCommandStr, L" " FORMAT_STR L" ", L"-socket"); for (SocketIndex = 0; SocketIndex < SocketIdsCount; SocketIndex++) { if (SocketIndex == 0) { pCommandStr = CatSPrintClean(pCommandStr, L"%d", pSocketIds[SocketIndex]); } else { pCommandStr = CatSPrintClean(pCommandStr, L",%d", pSocketIds[SocketIndex]); } } } FillCommandInput(pCommandStr, &ShowGoalCmdInput); ReturnCode = Parse(&ShowGoalCmdInput, &ShowGoalCmd); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed parsing command input"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } if (ShowGoalCmd.run == NULL) { NVDIMM_WARN("Couldn't find show -goal command"); ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, L"Loaded following pool configuration goal\n"); ExecuteCmd(&ShowGoalCmd); FREE_POOL_SAFE(pCommandStr); goto FinishSkipPrinterProcess; } Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FinishSkipPrinterProcess: FreeCommandInput(&ShowGoalCmdInput); FreeCommandStructure(&ShowGoalCmd); FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pCommandStr); FREE_POOL_SAFE(pLoadFilePath); FREE_POOL_SAFE(pFileString); FREE_POOL_SAFE(pSocketIds); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pLoadUserPath); FREE_POOL_SAFE(pUnitsStr); FREE_POOL_SAFE(pShowGoalOutputArgs); FREE_POOL_SAFE(pCommandStr); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Read and parse source file with Pool Goal structures to be loaded. @param[in] pFilePath Name is a pointer to a load file path @param[in] pDevicePath - handle to obtain generic path/location information concerning the physical device or logical device. The device path describes the location of the device the handle is for. @param[out] pFileString Buffer for Pool Goal configuration from file @retval EFI_SUCCESS File read and parse success @retval EFI_INVALID_PARAMETER At least one of parameters is NULL or format of configuration in file is not proper @retval EFI_INVALID_PARAMETER memory allocation failure **/ EFI_STATUS ParseSourceDumpFile( IN CHAR16 *pFilePath, IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath, OUT CHAR8 **pFileString ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT64 FileBufferSize = 0; UINT8 *pFileBuffer = NULL; UINT32 NumberOfChars = 0; if (pFilePath == NULL || pFileString == NULL) { NVDIMM_DBG("Invalid Pointer"); goto Finish; } #ifndef OS_BUILD if (pDevicePath == NULL) { NVDIMM_DBG("Invalid Pointer"); goto Finish; } #endif ReturnCode = FileRead(pFilePath, pDevicePath, MAX_CONFIG_DUMP_FILE_SIZE, FALSE, &FileBufferSize, (VOID **) &pFileBuffer); if (EFI_ERROR(ReturnCode) || pFileBuffer == NULL) { goto Finish; } /** Prepare memory for end of string char **/ *pFileString = ReallocatePool(FileBufferSize, FileBufferSize + sizeof('\0'), pFileBuffer); if (*pFileString == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } NumberOfChars = (UINT32)FileBufferSize / sizeof(**pFileString); /** Make read data from file as string **/ (*pFileString)[NumberOfChars] = '\0'; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the Load Goal command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterLoadGoalCommand( ) { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&LoadGoalCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/LoadGoalCommand.h000066400000000000000000000030151440615110200210400ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _LOAD_GOAL_COMMAND_ #define _LOAD_GOAL_COMMAND_ #include /** Register the Load Goal command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterLoadGoalCommand(); /** Read and parse source file with Pool Goal structures to be loaded. @param[in] pFilePath Name is a pointer to a load file path @param[in] pDevicePath - handle to obtain generic path/location information concerning the physical device or logical device. The device path describes the location of the device the handle is for. @param[out] pFileString Buffer for Pool Goal configuration from file @retval EFI_SUCCESS File read and parse success @retval EFI_INVALID_PARAMETER At least one of parameters is NULL or format of configuration in file is not proper @retval EFI_INVALID_PARAMETER memory allocation failure **/ EFI_STATUS ParseSourceDumpFile( IN CHAR16 *pFilePath, IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath, OUT CHAR8 **pFileString ); /** Execute the Load Goal command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS LoadGoal( IN struct Command *pCmd ); #endif /** _LOAD_GOAL_COMMAND_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/LoadSessionCommand.c000066400000000000000000000107721440615110200216040ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "Debug.h" #include "Types.h" #include "NvmInterface.h" #include "CommandParser.h" #include "LoadSessionCommand.h" #include "Common.h" #include "Convert.h" #include "Utility.h" #include #ifdef OS_BUILD #include "os.h" #endif #define SUCCESSFULLY_LOADED_BUFFER_MSG L"Successfully loaded %d bytes to session buffer." /** Command syntax definition **/ struct Command LoadSessionCommand = { LOAD_VERB, //!< verb { //!< options {L"", SOURCE_OPTION, L"", SOURCE_OPTION_HELP, L"Source of the Session to load", FALSE, ValueRequired}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, { //!< targets {SESSION_TARGET, L"", L"", TRUE, ValueEmpty} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Load a recorded (PBR) session into memory for playback.", //!< help LoadSession, TRUE, TRUE }; /** Execute the Load Session command **/ EFI_STATUS LoadSession( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol = NULL; EFI_DEVICE_PATH_PROTOCOL *pDevicePathProtocol = NULL; CHAR16 *pLoadFilePath = NULL; CHAR16 *pLoadUserPath = NULL; UINT64 FileBufferSize = 0; UINT8 *pFileBuffer = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; NVDIMM_ENTRY(); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; Print(FORMAT_STR_NL, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; //If Windows, check for admin privilege needed to update registry for PBR state CHECK_WIN_ADMIN_PERMISSIONS(); // Check -source option if (containsOption(pCmd, SOURCE_OPTION)) { pLoadUserPath = getOptionValue(pCmd, SOURCE_OPTION); if (pLoadUserPath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Could not get -source value. Out of memory"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } } // NvmDimmConfigProtocol required ReturnCode = OpenNvmDimmProtocol(gNvmDimmPbrProtocolGuid, (VOID **)&pNvmDimmPbrProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } pLoadFilePath = AllocateZeroPool(OPTION_VALUE_LEN * sizeof(*pLoadFilePath)); if (pLoadFilePath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = GetDeviceAndFilePath(pLoadUserPath, pLoadFilePath, &pDevicePathProtocol); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_GET_FILE_PATH, ReturnCode); goto Finish; } ReturnCode = FileRead(pLoadFilePath, pDevicePathProtocol, 0, TRUE, &FileBufferSize, (VOID **)&pFileBuffer); if (EFI_ERROR(ReturnCode) || pFileBuffer == NULL) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_READ_FILE); goto Finish; } //session module responsible for freeing buffer. ReturnCode = pNvmDimmPbrProtocol->PbrSetSession(pFileBuffer, (UINT32)FileBufferSize); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_SET_SESSION_BUFFER); goto Finish; } //reset the tagid to 0 (first tag) PbrDcpmmSerializeTagId(0); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, SUCCESSFULLY_LOADED_BUFFER_MSG, (UINT32)FileBufferSize); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pLoadFilePath); FREE_POOL_SAFE(pLoadUserPath); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the Load Session command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterLoadSessionCommand( ) { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&LoadSessionCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/LoadSessionCommand.h000066400000000000000000000013351440615110200216040ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _LOAD_SESSION_COMMAND_ #define _LOAD_SESSION_COMMAND_ #include /** Register the Load Pbr command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterLoadSessionCommand(); /** Execute the Load Session CMD @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS LoadSession( IN struct Command *pCmd ); #endif /** _LOAD_SESSION_COMMAND_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/NvmDimmCli.c000066400000000000000000001106261440615110200200600ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "NvmDimmCli.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CommandParser.h" #include "ShowDimmsCommand.h" #include "ShowSocketsCommand.h" #include "SetDimmCommand.h" #include "DeleteDimmCommand.h" #include "ShowRegionsCommand.h" #include "ShowAcpiCommand.h" #include "ShowSmbiosCommand.h" #include "LoadCommand.h" #include "SetSensorCommand.h" #include "ShowSensorCommand.h" #include "CreateGoalCommand.h" #include "ShowGoalCommand.h" #include "DeleteGoalCommand.h" #include "DumpGoalCommand.h" #include "LoadGoalCommand.h" #include "StartDiagnosticCommand.h" #include "ShowNamespaceCommand.h" #include "CreateNamespaceCommand.h" #include "DeleteNamespaceCommand.h" #include "ShowErrorCommand.h" #include "ShowCelCommand.h" #include "ShowTopologyCommand.h" #include "DumpDebugCommand.h" #include "ShowMemoryResourcesCommand.h" #include "ShowSystemCapabilitiesCommand.h" #include "ShowRegisterCommand.h" #include "Common.h" #include "ShowFirmwareCommand.h" #include "ShowPcdCommand.h" #include "StartFormatCommand.h" #include "ShowPreferencesCommand.h" #include "SetPreferencesCommand.h" #include "ShowPerformanceCommand.h" #include "ShowCmdAccessPolicyCommand.h" #include "DeletePcdCommand.h" #include #include "StartSessionCommand.h" #include "StopSessionCommand.h" #include "DumpSessionCommand.h" #include "ShowSessionCommand.h" #include "LoadSessionCommand.h" #ifdef OS_BUILD #include "os_efi_shell_parameters_protocol.h" #include #else #include #endif #include #if _BullseyeCoverage #ifndef OS_BUILD extern int cov_dumpData(void); #endif // !OS_BUILD #endif // _BullseyeCoverage #ifdef __MFG__ #include #endif #ifdef OS_BUILD #include "DumpSupportCommand.h" #include extern void nvm_current_cmd(struct Command Command); extern BOOLEAN ConfigIsDdrtProtocolDisabled(); extern BOOLEAN ConfigIsLargePayloadDisabled(); extern int g_fast_path; #else #include "DeletePcdCommand.h" EFI_GUID gNvmDimmConfigProtocolGuid = EFI_DCPMM_CONFIG2_PROTOCOL_GUID; EFI_GUID gIntelDimmConfigVariableGuid = INTEL_DIMM_CONFIG_VARIABLE_GUID; EFI_GUID gIntelDimmPbrTagIdVariableguid = INTEL_DIMM_PBR_TAGID_VARIABLE_GUID; EFI_GUID gNvmDimmPbrProtocolGuid = EFI_DCPMM_PBR_PROTOCOL_GUID; #endif EFI_GUID gNvmDimmDriverHealthGuid = EFI_DRIVER_HEALTH_PROTOCOL_GUID; extern EFI_SHELL_INTERFACE *mEfiShellInterface; #ifdef OS_BUILD EFI_HANDLE gNvmDimmCliHiiHandle = (EFI_HANDLE)0x1; extern EFI_SHELL_PARAMETERS_PROTOCOL gOsShellParametersProtocol; extern EFI_DRIVER_BINDING_PROTOCOL gNvmDimmDriverDriverBinding; #else EFI_HANDLE gNvmDimmCliHiiHandle = NULL; #ifndef MDEPKG_NDEBUG extern volatile UINT32 _gPcd_BinaryPatch_PcdDebugPrintErrorLevel; #endif //MDEPKG_NDEBUG #endif #define NVMDIMM_CLI_HII_GUID \ { 0x26e4ac23, 0xd32f, 0x4788, {0x83, 0x95, 0xb0, 0x2a, 0x33, 0x0c, 0x28, 0x26}} EFI_GUID gNvmDimmCliHiiGuid = NVMDIMM_CLI_HII_GUID; /* Local fns */ static EFI_STATUS showVersion(struct Command *pCmd); static EFI_STATUS GetPbrMode(UINT32 *Mode); static EFI_STATUS SetPbrTag(CHAR16 *pName, CHAR16 *pDescription); static EFI_STATUS ResetPbrSession(UINT32 TagId); static EFI_STATUS SetDefaultProtocolAndPayloadSizeOptions(); #ifndef OS_BUILD #ifndef MDEPKG_NDEBUG static EFI_STATUS GetDriverDebugPrintVerbosity(UINT32 *pErrorLevel); static EFI_STATUS SetDriverDebugPrintVerbosity(UINT32 ErrorLevel); #endif #endif /** Supported commands Display CLI application help **/ struct Command HelpCommand = { HELP_VERB, //!< verb { {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD {OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired }, //!< options #endif // OS_BUILD {L"", L"", L"", L"",L"", FALSE, ValueOptional} }, //!< options {{L"", L"", L"", FALSE, ValueOptional}}, //!< targets {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Display the CLI help.", //!< help showHelp, //!< run function TRUE }; /** Display the CLI application version **/ struct Command VersionCommand = { VERSION_VERB, //!< verb { {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #else {L"", L"", L"", L"", FALSE, ValueOptional} //!< options #endif }, //!< options {{L"", L"", L"", FALSE, ValueOptional}}, //!< targets {{L"", L"", L"", FALSE}}, //!< properties L"Display the CLI version.", //!< help showVersion, //!< run function TRUE, //!< support printer TRUE //!< skip initialization }; BOOLEAN HelpRequested = FALSE; BOOLEAN FullHelpRequested = FALSE; /** Reviews the passed tokens for help|-h|-help flags and prepares the token order for proper display **/ VOID FixHelp(CHAR16** ppTokens, UINT32* pCount){ UINT32 Index = 0; UINT32 Index2 = 0; UINT32 HelpIndex = 0; CHAR16 *pHelpTokenTmp = NULL; UINT32 HelpFlags = 0; BOOLEAN HelpTokenIndexWrong = FALSE; HelpRequested = FALSE; FullHelpRequested = FALSE; if (ppTokens == NULL || pCount == NULL || *pCount <= 0) { return; } //look for simple requests for global help if (0 == StrICmp(ppTokens[0], HELP_VERB) || 0 == StrICmp(ppTokens[0], HELP_OPTION) || 0 == StrICmp(ppTokens[0], HELP_OPTION_SHORT)) { HelpRequested = TRUE; FullHelpRequested = TRUE; for (Index = 1; Index < *pCount; Index++) { FREE_POOL_SAFE(ppTokens[Index]); } *pCount = 1; return; } //Examine the post-verb to determine if/where the help token(s) are for (Index = 1; Index < *pCount; Index++) { if (0 == StrICmp(ppTokens[Index], HELP_VERB) || 0 == StrICmp(ppTokens[Index], HELP_OPTION) || 0 == StrICmp(ppTokens[Index], HELP_OPTION_SHORT)) { HelpFlags++; HelpRequested = TRUE; if (HelpFlags == 1) { //retain the help token, but change to the short version ppTokens[Index][0] = '-'; ppTokens[Index][1] = 'h'; ppTokens[Index][2] = 0; HelpIndex = Index; pHelpTokenTmp = ppTokens[Index]; HelpTokenIndexWrong = Index > 1; } else { //dispose of duplicate help flags FREE_POOL_SAFE(ppTokens[Index]); } //create a 'hole' in the array ppTokens[Index] = NULL; } } //nothing to do if (HelpFlags == 0) return; if (TRUE == HelpTokenIndexWrong) { //make a spot at slot 1 for the help token for (Index = HelpIndex; Index > 1; Index--) { ppTokens[Index] = ppTokens[Index - 1]; } } //move the token to the right spot ppTokens[1] = pHelpTokenTmp; //collapse any 'holes' in the array (caused by multiple help flags) for (Index = 1; Index < *pCount; Index++) { if (ppTokens[Index] == NULL) { for (Index2 = Index + 1; Index2 < *pCount; Index2++) { if (ppTokens[Index2] != NULL) { ppTokens[Index] = ppTokens[Index2]; ppTokens[Index2] = NULL; break; } } } } //adjust the count to account for removed parameters for (Index = 1; Index < *pCount; Index++) { if (ppTokens[Index] == NULL) { break; } } //set the new count *pCount = Index; } VOID PrintErrorMsg(CHAR16 *ErrorMsg, BOOLEAN ForESX) { CHAR16 *pSubString = NULL; CHAR16 *pSubStringEnd = NULL; CHAR16 *pStringWalker = NULL; if (ForESX) { pSubString = AllocateZeroPool(200 *sizeof(CHAR16)); if (NULL == pSubString) { goto StandardPrint; } pStringWalker = ErrorMsg; //pSubStringBegin = pSubString; pSubStringEnd = pSubString; while (L'\0' != *pStringWalker) { while ((L'\n' != *pStringWalker) && (L'\0' != *pStringWalker)) { *pSubStringEnd = *pStringWalker; ++pSubStringEnd; ++pStringWalker; } if (pSubString != pSubStringEnd) { *pSubStringEnd = L'\n'; ++pSubStringEnd; *pSubStringEnd = L'\0'; // make sure string is NULL terminated Print(L"ERROR: "); LongPrint(pSubString); } ++pStringWalker; if (L'\n' == *pStringWalker) { Print(L"ERROR:\n"); } pSubStringEnd = pSubString; } goto finish; } StandardPrint: LongPrint(ErrorMsg); Print(L"\n"); finish: FREE_POOL_SAFE(pSubString); } /* ./ * The entry point for the application. * * @param[in] ImageHandle The firmware allocated handle for the EFI image. * @param[in] pSystemTable A pointer to the EFI System Table. * * @retval EFI_SUCCESS The entry point is executed successfully. * @retval other Some error occurs when executing this entry point. */ EFI_STATUS EFIAPI UefiMain( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *pSystemTable ) { EFI_STATUS Rc = EFI_SUCCESS; struct CommandInput Input; struct Command Command; INT32 Index = 0; CHAR16 *pLine = NULL; BOOLEAN MoreInput = TRUE; BOOLEAN HelpShown = FALSE; UINTN Argc = 0; CHAR16 **ppArgv = NULL; UINT32 NextId = 0; #ifdef OS_BUILD BOOLEAN IsVersionCommand = FALSE; #else SHELL_FILE_HANDLE StdIn = NULL; #ifndef MDEPKG_NDEBUG UINT32 DefaultErrorLevel = 0; GetDriverDebugPrintVerbosity(&DefaultErrorLevel); UINT32 UpdatedErrorLevel = DefaultErrorLevel; #endif #endif NVDIMM_ENTRY(); ZeroMem(&Input, sizeof(Input)); ZeroMem(&Command, sizeof(Command)); #ifndef OS_BUILD /* only support EFI shell 2.0 */ Rc = ShellInitialize(); if (EFI_ERROR(Rc)) { Rc = EFI_UNSUPPORTED; NVDIMM_WARN("ShellInitialize failed, rc = 0x%llx", Rc); Print(L"Error: EFI Shell 2.0 is required to run this application\n"); goto Finish; } #endif if (gEfiShellParametersProtocol == NULL) { Rc = EFI_UNSUPPORTED; #ifndef OS_BUILD NVDIMM_WARN("ShellInitialize succeeded but the shell interface and parameters protocols do not exist"); Print(L"Error: EFI Shell 2.0 is required to run this application.\n"); #else NVDIMM_WARN("Shell interface and parameters protocols do not exist"); #endif goto Finish; } #ifndef OS_BUILD StdIn = gEfiShellParametersProtocol->StdIn; #endif Argc = gEfiShellParametersProtocol->Argc; ppArgv = gEfiShellParametersProtocol->Argv; for (Index = 1; Index < Argc; Index++) { #ifndef OS_BUILD #ifndef MDEPKG_NDEBUG /** For UEFI pre-parse CLI arguments for verbose logging **/ if (0 == StrICmp(ppArgv[Index], VERBOSE_OPTION) || 0 == StrICmp(ppArgv[Index], VERBOSE_OPTION_SHORT)) { PatchPcdSet32(PcdDebugPrintErrorLevel, DEBUG_VERBOSE); UpdatedErrorLevel = DefaultErrorLevel | DEBUG_VERBOSE; SetDriverDebugPrintVerbosity(UpdatedErrorLevel); } #endif #endif /** Need to set some flags in the case that the user wants help, but there are no DIMMs in the system **/ if (0 == StrICmp(ppArgv[Index], HELP_VERB) || 0 == StrICmp(ppArgv[Index], HELP_OPTION) || 0 == StrICmp(ppArgv[Index], HELP_OPTION_SHORT)) { HelpRequested = TRUE; if (Argc == 2) { FullHelpRequested = TRUE; } } } if (Argc == 1) { #ifndef OS_BUILD /* Verify input was not redirected from a file */ if (ShellGetFileInfo(StdIn) == NULL) { #endif HelpRequested = TRUE; FullHelpRequested = TRUE; #ifndef OS_BUILD } #endif } #ifndef OS_BUILD BOOLEAN Ascii = FALSE; UINTN HandleCount = 0; EFI_HANDLE *pHandleBuffer = NULL; CHAR16 *pCurrentDriverName; EFI_COMPONENT_NAME_PROTOCOL *pComponentName = NULL; SetSerialAttributes(); #else EFI_HANDLE FakeBindHandle = (EFI_HANDLE)0x1; #endif UINT32 Mode = PBR_NORMAL_MODE; CHAR16 *pTagDescription = NULL; if (!HelpRequested) { //get the current pbr mode (playback/record/normal) Rc = GetPbrMode(&Mode); if (EFI_ERROR(Rc) && (EFI_NOT_FOUND != Rc)) { goto Finish; } if (Mode == PBR_RECORD_MODE) { Print(L"Warning - Executing in recording mode!\n\n"); } else if (Mode == PBR_PLAYBACK_MODE) { Print(L"Warning - Executing in playback mode!\n\n"); } Rc = SetDefaultProtocolAndPayloadSizeOptions(); if (EFI_ERROR(Rc) && (EFI_NOT_FOUND != Rc)) { goto Finish; } } Index = 0; /** Print runtime function address to ease calculation of GDB symbol loading offset. **/ NVDIMM_DBG_CLEAN("NvmDimmCliEntryPoint=0x%016lx\n", &UefiMain); #ifndef OS_BUILD /* with shell support level 3 */ if (PcdGet8(PcdShellSupportLevel) < 3) { Rc = EFI_UNSUPPORTED; NVDIMM_WARN("ShellSupport level %d too low", PcdGet8(PcdShellSupportLevel)); Print(L"Error: EFI Shell support level 3 is required to run this application\n"); goto Finish; } if (FALSE == HelpRequested) { gNvmDimmCliHiiHandle = HiiAddPackages(&gNvmDimmCliHiiGuid, ImageHandle, ipmctlStrings, NULL); if (gNvmDimmCliHiiHandle == NULL) { NVDIMM_WARN("Unable to add string package to Hii"); goto Finish; } // Check for NVM Protocol Rc = gBS->LocateHandleBuffer(ByProtocol, &gNvmDimmConfigProtocolGuid, NULL, &HandleCount, &pHandleBuffer); if (EFI_NOT_FOUND != Rc && (EFI_ERROR(Rc) || HandleCount != 1)) { if (Rc == EFI_NOT_FOUND) { Print(FORMAT_STR_NL, CLI_ERR_FAILED_TO_FIND_PROTOCOL); goto Finish; } Print(FORMAT_STR_NL, CLI_ERR_OPENING_CONFIG_PROTOCOL); Rc = EFI_NOT_FOUND; goto Finish; } Rc = OpenNvmDimmProtocol( gEfiComponentNameProtocolGuid, (VOID**)&pComponentName, NULL); if (EFI_ERROR(Rc) && EFI_NOT_FOUND != Rc) { NVDIMM_DBG("Failed to open the Component Name protocol, error = " FORMAT_EFI_STATUS "", Rc); goto Finish; } if (pComponentName != NULL) { //Get current driver name Rc = pComponentName->GetDriverName( pComponentName, "eng", &pCurrentDriverName); if (EFI_ERROR(Rc)) { NVDIMM_DBG("Could not get the driver name, error = " FORMAT_EFI_STATUS "", Rc); goto Finish; } //Compare to the CLI version and print warning if there is a version mismatch if (StrCmp(PMEM_MODULE_NAME NVMDIMM_VERSION_STRING L" Driver", pCurrentDriverName) != 0) { Print(FORMAT_STR_NL, CLI_WARNING_CLI_DRIVER_VERSION_MISMATCH); } } } #else // if help is requested Register all the commands so parsing will work later if (g_basic_commands && !HelpRequested) { Rc = RegisterNonAdminUserCommands(); if (EFI_ERROR(Rc)) { goto Finish; } } else #endif //OS_BUILD { Rc = RegisterCommands(); if (EFI_ERROR(Rc)) { goto Finish; } } while (MoreInput) { Input.TokenCount = 0; #ifndef OS_BUILD /* user entered a command on the command pLine */ if (ShellGetFileInfo(StdIn) == NULL) { #endif /* 1st arg is the name of the app, so skip it */ if (Argc > 1 && FALSE == FullHelpRequested) { MoreInput = FALSE; /* only one command is supported on the command pLine */ for (Index = 1; Index < Argc; Index++) { pLine = CatSPrintClean(pLine, FORMAT_STR_SPACE, ppArgv[Index]); if (pLine == NULL) { Print(FORMAT_STR_NL, CLI_ERR_OUT_OF_MEMORY); Rc = EFI_OUT_OF_RESOURCES; goto FinishAfterRegCmds; } } FillCommandInput(pLine, &Input); if (Input.ppTokens == NULL) { Print(FORMAT_STR_NL, CLI_ERR_OUT_OF_MEMORY); Rc = EFI_OUT_OF_RESOURCES; goto FinishAfterRegCmds; } } else { /* user did not enter a command */ showHelp(NULL); HelpShown = TRUE; break; } #ifndef OS_BUILD } else { /* input was redirected from a file */ pLine = NULL; pLine = ShellFileHandleReturnLine(StdIn, &Ascii); if (pLine == NULL || StrLen(pLine) == 0) { /* line was empty, go to next pLine */ if (ShellFileHandleEof(StdIn)) { MoreInput = FALSE; } if (pLine != NULL) { FreePool(pLine); pLine = NULL; } continue; } /* parse the line into individual tokens */ FillCommandInput(pLine, &Input); if (Input.ppTokens == NULL) { Print(FORMAT_STR_NL, CLI_ERR_OUT_OF_MEMORY); Rc = EFI_OUT_OF_RESOURCES; goto FinishAfterRegCmds; } if (ShellFileHandleEof(StdIn)) { MoreInput = FALSE; } } #endif /* Fix the passed tokens as needed */ FixHelp(Input.ppTokens, &Input.TokenCount); if (TRUE == FullHelpRequested) { showHelp(NULL); HelpShown = TRUE; break; } /* run the command */ Rc = Parse(&Input, &Command); if (!HelpRequested) { if (PBR_NORMAL_MODE != Mode && !Command.ExcludeDriverBinding) { if (PBR_RECORD_MODE == Mode) { pTagDescription = CatSPrint(NULL, L"%d", Rc); SetPbrTag(pLine, pTagDescription); FREE_POOL_SAFE(pTagDescription); } else { //CLI is responsible for tracking the tagid. //The id is saved to a non-persistent volatile store, and is incremented //after each CLI cmd invocation. Given we have the tagid that should //be executed next, explicitly reset the pbr session to that id before //running the cmd. PbrDcpmmDeserializeTagId(&NextId, 0); ResetPbrSession(NextId); PbrDcpmmSerializeTagId(NextId + 1); } } } if (!EFI_ERROR(Rc)) { /* parse success, now run the command */ if (Command.ShowHelp) { showHelp(&Command); HelpShown = TRUE; } else { #ifdef OS_BUILD // different handling of returncodes for version command so it works for regular users IsVersionCommand = (StrnCmp(Command.verb, VERSION_VERB, VERB_LEN) == 0); if (!Command.ExcludeDriverBinding && !g_fast_path) { Rc = NvmDimmDriverDriverBindingStart(&gNvmDimmDriverDriverBinding, FakeBindHandle, NULL); if (EFI_ERROR(Rc) && !IsVersionCommand) { NVDIMM_ERR("Issue with driver initialization"); Print(GetSingleNvmStatusCodeMessage(gNvmDimmCliHiiHandle,GuessNvmStatusFromReturnCode(Rc))); Print(FORMAT_NL); } } if (!EFI_ERROR(Rc) || IsVersionCommand) { #else if (!EFI_ERROR(Rc)) { #endif Rc = ExecuteCmd(&Command); } #ifdef OS_BUILD if (!Command.ExcludeDriverBinding && !g_fast_path) { NvmDimmDriverDriverBindingStop(&gNvmDimmDriverDriverBinding, FakeBindHandle, 0, NULL); } #endif } if (EFI_ERROR(Rc)) { MoreInput = FALSE; /* stop on failures */ } } else { /* syntax error */ #ifdef OS_BUILD PrintErrorMsg(getSyntaxError(), is_ESX_output_requested()); #else PrintErrorMsg(getSyntaxError(), FALSE); #endif MoreInput = FALSE; /* stop on failures */ } #ifdef OS_BUILD nvm_current_cmd(Command); #endif FreeCommandInput(&Input); FreeCommandStructure(&Command); } /* end while more input */ FinishAfterRegCmds: /* clean up */ FreeCommands(); Finish: FREE_POOL_SAFE(pLine); FreeCommandInput(&Input); if (gNvmDimmCliHiiHandle != NULL) { HiiRemovePackages(gNvmDimmCliHiiHandle); } #if _BullseyeCoverage #ifndef OS_BUILD cov_dumpData(); #endif // !OS_BUILD #endif // _BullseyeCoverage //if help was displayed and not explicitly requested, ensure an error is returned if (TRUE == HelpShown && FALSE == HelpRequested && FALSE == FullHelpRequested && EFI_SUCCESS == Rc) { Rc = EFI_INVALID_PARAMETER; } #ifndef OS_BUILD #ifndef MDEPKG_NDEBUG //Reset driver debug print error level to original default value, otherwise will persist across cli invocations if (DefaultErrorLevel != UpdatedErrorLevel) { SetDriverDebugPrintVerbosity(DefaultErrorLevel); } #endif #endif NVDIMM_EXIT_I64(Rc); return Rc; } /** Register basic commands on the commands list for non-root users @retval a return code from called functions **/ EFI_STATUS RegisterNonAdminUserCommands( ) { EFI_STATUS Rc; NVDIMM_ENTRY(); Rc = RegisterCommand(&VersionCommand); NVDIMM_EXIT_I64(Rc); return Rc; } /** Register commands on the commands list @retval a return code from called functions **/ EFI_STATUS RegisterCommands( ) { EFI_STATUS Rc; NVDIMM_ENTRY(); /* Always register */ Rc = RegisterCommand(&HelpCommand); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterNonAdminUserCommands(); if (EFI_ERROR(Rc)) { goto done; } // Dimm Discovery Commands Rc = RegisterShowTopologyCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterShowSocketsCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterShowDimmsCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterShowMemoryResourcesCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterShowSystemCapabilitiesCommand(); if (EFI_ERROR(Rc)) { goto done; } // Provisioning Commands Rc = RegisterCreateGoalCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterShowGoalCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterDumpGoalCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterLoadGoalCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterDeleteGoalCommand(); if (EFI_ERROR(Rc)) { goto done; } // Security Commands Rc = RegisterSetDimmCommand(); if (EFI_ERROR(Rc)) { goto done; } #ifndef OS_BUILD Rc = RegisterDeleteDimmCommand(); if (EFI_ERROR(Rc)) { goto done; } #endif // Persistent Memory Provisioning Commands Rc = RegisterShowRegionsCommand(); if (EFI_ERROR(Rc)) { goto done; } #ifndef OS_BUILD Rc = RegisterShowNamespaceCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterCreateNamespaceCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterDeleteNamespaceCommand(); if (EFI_ERROR(Rc)) { goto done; } #endif // Instrumentation Commands Rc = RegisterShowSensorCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterSetSensorCommand(); if (EFI_ERROR(Rc)) { goto done; } #ifndef __MFG__ // Mfg has a low latency version of this command in RegisterMfgCommands() Rc = RegisterShowPerformanceCommand(); if (EFI_ERROR(Rc)) { goto done; } #endif // !__MFG__ #ifdef OS_BUILD #ifdef __MFG__ Rc = RegisterMfgCommands(); if (EFI_ERROR(Rc)) { goto done; } #endif // __MFG__ #endif // OS_BUILD // Support and Maintenance Commands Rc = RegisterShowFirmwareCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterLoadCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterShowPreferencesCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterSetPreferencesCommand(); if (EFI_ERROR(Rc)) { goto done; } #ifdef OS_BUILD Rc = RegisterDumpSupportCommand(); if (EFI_ERROR(Rc)) { goto done; } #endif // OS_BUILD // Debug Commands Rc = RegisterStartDiagnosticCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterShowErrorCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterDumpDebugCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterShowAcpiCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterShowPcdCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterDeletePcdCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterShowCmdAccessPolicyCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterShowCelCommand(); if (EFI_ERROR(Rc)) { goto done; } #ifndef OS_BUILD /* Debug Utility commands */ Rc = registerShowSmbiosCommand(); if (EFI_ERROR(Rc)) { goto done; } #ifndef MDEPKG_NDEBUG Rc = RegisterShowRegisterCommand(); if (EFI_ERROR(Rc)) { goto done; } #endif #ifdef FORMAT_SUPPORTED Rc = RegisterStartFormatCommand(); if (EFI_ERROR(Rc)) { goto done; } #endif #endif // PBR Commands Rc = RegisterStartSessionCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterDumpSessionCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterLoadSessionCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterShowSessionCommand(); if (EFI_ERROR(Rc)) { goto done; } Rc = RegisterStopSessionCommand(); if (EFI_ERROR(Rc)) { goto done; } done: NVDIMM_EXIT_I64(Rc); return Rc; } /* * Print the CLI application help */ EFI_STATUS showHelp(struct Command *pCmd) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 *pHelp = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; #ifdef OS_BUILD UINT8 Index = 0; BOOLEAN FormatAsESXErrorMsg = FALSE; #endif NVDIMM_ENTRY(); if (NULL != pCmd) { pPrinterCtx = pCmd->pPrintCtx; } if ((pCmd == NULL) || (StrCmp(pCmd->verb, HELP_VERB) == 0 && pCmd->ShowHelp == FALSE)) { #ifndef OS_BUILD //Page break option only for UEFI ShellSetPageBreakMode(TRUE); #endif PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_SPACE FORMAT_STR_NL_NL L" Usage: " FORMAT_STR L" [][][]\n\nCommands:\n", PRODUCT_NAME, APP_DESCRIPTION, EXE_NAME); pHelp = getOverallCommandHelp(); } else { pHelp = getCommandHelp(pCmd, TRUE); } if (pHelp != NULL) { #ifdef OS_BUILD if (pPrinterCtx != NULL) { #endif PRINTER_SET_MSG(pPrinterCtx, ReturnCode, pHelp); #ifdef OS_BUILD } else { // if Printer is not setup then may need to do special handling for ESX if ((pPrinterCtx == NULL) && (pCmd != NULL) && (pCmd->options != NULL)) { for (Index = 0; Index < MAX_OPTIONS; Index++) { if ((StrICmp(pCmd->options[Index].OptionName, OUTPUT_OPTION) == 0) || (StrICmp(pCmd->options[Index].OptionNameShort, OUTPUT_OPTION_SHORT) == 0)) { if ((StrICmp(pCmd->options[Index].pOptionValueStr, OUTPUT_OPTION_ESX_XML) == 0) || (StrICmp(pCmd->options[Index].pOptionValueStr, OUTPUT_OPTION_ESX_TABLE_XML) == 0)) { FormatAsESXErrorMsg = TRUE; break; } } } } // Only do alternative output if for ESX to be sure not to change output for others if (FormatAsESXErrorMsg) { PrintErrorMsg(pHelp, FormatAsESXErrorMsg); } else { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, pHelp); } } #endif FreePool(pHelp); } if (NULL != pPrinterCtx) { PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #define DS_ROOT_PATH L"/SoftwareList" #define DS_SOFTWARE_PATH L"/SoftwareList/Software" #define DS_SOFTWARE_INDEX_PATH L"/SoftwareList/Software[%d]" PRINTER_LIST_ATTRIB ShowVersionListAttributes = { { { L"Software", //GROUP LEVEL TYPE L"$(Component) Version $(Version)", //NULL or GROUP LEVEL HEADER NULL, //NULL or KEY VAL FORMAT STR L"Component;Version" //NULL or IGNORE KEY LIST (K1;K2) } } }; PRINTER_DATA_SET_ATTRIBS ShowVersionDataSetAttribs = { &ShowVersionListAttributes, NULL }; /* * Set a PBR session tag */ EFI_STATUS SetPbrTag(CHAR16 *pName, CHAR16 *pDescription) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol = NULL; ReturnCode = OpenNvmDimmProtocol(gNvmDimmPbrProtocolGuid, (VOID **)&pNvmDimmPbrProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; Print(CLI_ERR_OPENING_PBR_PROTOCOL); goto Finish; } ReturnCode = pNvmDimmPbrProtocol->PbrSetTag(PBR_DCPMM_CLI_SIG, pName, pDescription, NULL); if (EFI_ERROR(ReturnCode)) { Print(CLI_ERR_FAILED_TO_SET_SESSION_TAG); goto Finish; } Finish: return ReturnCode; } /* * Get the current PBR session mode (playback/record) */ EFI_STATUS GetPbrMode(UINT32 *Mode) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol = NULL; *Mode = PBR_NORMAL_MODE; ReturnCode = OpenNvmDimmProtocol(gNvmDimmPbrProtocolGuid, (VOID **)&pNvmDimmPbrProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; NVDIMM_DBG("Failed to open the PBR protocol, error = " FORMAT_EFI_STATUS, ReturnCode); goto Finish; } ReturnCode = pNvmDimmPbrProtocol->PbrGetMode(Mode); if (EFI_ERROR(ReturnCode)) { Print(CLI_ERR_FAILED_TO_GET_PBR_MODE); goto Finish; } Finish: return ReturnCode; } /* * Reset the session to a specified tag */ EFI_STATUS ResetPbrSession(UINT32 TagId) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol = NULL; ReturnCode = OpenNvmDimmProtocol(gNvmDimmPbrProtocolGuid, (VOID **)&pNvmDimmPbrProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; Print(CLI_ERR_OPENING_PBR_PROTOCOL); goto Finish; } ReturnCode = pNvmDimmPbrProtocol->PbrResetSession(TagId); if (EFI_ERROR(ReturnCode)) { Print(CLI_ERR_FAILED_TO_SET_SESSION_TAG); goto Finish; } Finish: return ReturnCode; } /* * Print the CLI app version */ EFI_STATUS showVersion(struct Command *pCmd) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; CHAR16 *pPath = NULL; UINT32 DimmIndex = 0; UINT32 DimmCount = 0; PRINT_CONTEXT *pPrinterCtx = NULL; DIMM_INFO *pDimms = NULL; UINT32 DimmFromTheFutureCount = 0; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; #if !defined(__LINUX__) CHAR16 ApiVersion[FW_API_VERSION_LEN] = { 0 }; #endif NVDIMM_ENTRY(); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (ReturnCode != EFI_NOT_FOUND && EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } if (ReturnCode != EFI_NOT_FOUND) { #if !defined(__LINUX__) ReturnCode = pNvmDimmConfigProtocol->GetDriverApiVersion(pNvmDimmConfigProtocol, ApiVersion); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } #endif } PRINTER_BUILD_KEY_PATH(pPath, DS_SOFTWARE_INDEX_PATH, 0); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"Component", PRODUCT_NAME L" " APP_DESCRIPTION); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"Version", NVMDIMM_VERSION_STRING); #if !defined(__LINUX__) PRINTER_BUILD_KEY_PATH(pPath, DS_SOFTWARE_INDEX_PATH, 1); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"Component", PRODUCT_NAME L" " DRIVER_API_DESCRIPTION); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"Version", ApiVersion); #endif if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } /*Check FIS against compiled version in this SW... warn if the FIS version from FW is > version from this SW*/ ReturnCode = pNvmDimmConfigProtocol->GetDimmCount(pNvmDimmConfigProtocol, &DimmCount); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } pDimms = AllocateZeroPool(sizeof(*pDimms) * DimmCount); if (NULL == pDimms) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetDimms(pNvmDimmConfigProtocol, DimmCount, DIMM_INFO_CATEGORY_NONE, pDimms); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_ABORTED; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_WARN("Failed to retrieve the DCPMM inventory found in NFIT"); goto Finish; } for (DimmIndex = 0; DimmIndex < DimmCount; DimmIndex++) { ReturnCode = GetPreferredDimmIdAsString(pDimms[DimmIndex].DimmHandle, pDimms[DimmIndex].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_ABORTED; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to determine DimmId for " PMEM_MODULE_STR L"%d", pDimms[DimmIndex].DimmHandle); goto Finish; } if (pDimms[DimmIndex].FwVer.FwApiMajor > MAX_FIS_SUPPORTED_BY_THIS_SW_MAJOR || (pDimms[DimmIndex].FwVer.FwApiMajor == MAX_FIS_SUPPORTED_BY_THIS_SW_MAJOR && pDimms[DimmIndex].FwVer.FwApiMinor > MAX_FIS_SUPPORTED_BY_THIS_SW_MINOR)) { DimmFromTheFutureCount++; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, PMEM_MODULE_STR L" " FORMAT_STR L" supports FIS %d.%d\r\n", DimmStr, pDimms[DimmIndex].FwVer.FwApiMajor, pDimms[DimmIndex].FwVer.FwApiMinor); } } if (DimmFromTheFutureCount > 0) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"This ipmctl software version predates the firmware interface specification version (FIS | FWAPIVersion: %d.%d) for %d " PMEM_MODULE_STR L"(s). It is recommended to update ipmctl.\r\n", MAX_FIS_SUPPORTED_BY_THIS_SW_MAJOR, MAX_FIS_SUPPORTED_BY_THIS_SW_MINOR, DimmFromTheFutureCount); } //Specify table attributes PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowVersionDataSetAttribs); //Force as list PRINTER_ENABLE_LIST_TABLE_FORMAT(pPrinterCtx); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pPath); FREE_POOL_SAFE(pDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } EFI_STATUS SetDefaultProtocolAndPayloadSizeOptions() { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS Attribs; #ifdef OS_BUILD // Default value for ini file (OS only) is set in ipmctl_default.h BOOLEAN IsDdrtProtocolDisabled = ConfigIsDdrtProtocolDisabled(); BOOLEAN IsLargePayloadDisabled = ConfigIsLargePayloadDisabled(); #endif // OS_BUILD NVDIMM_ENTRY(); // Clearly set defaults. Auto = no restrictions Attribs.Protocol = FisTransportAuto; Attribs.PayloadSize = FisTransportSizeAuto; #ifdef OS_BUILD // Equivalent to passing "-smbus" if (IsDdrtProtocolDisabled) { Attribs.Protocol = FisTransportSmbus; Attribs.PayloadSize = FisTransportSizeSmallMb; } if (IsLargePayloadDisabled) { Attribs.PayloadSize = FisTransportSizeSmallMb; } #endif // OS_BUILD ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } CHECK_RESULT(pNvmDimmConfigProtocol->SetFisTransportAttributes(pNvmDimmConfigProtocol, Attribs), Finish); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #ifndef OS_BUILD #ifndef MDEPKG_NDEBUG EFI_STATUS GetDriverDebugPrintVerbosity(UINT32 *pErrorLevel) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; NVDIMM_ENTRY(); if (NULL == pErrorLevel) { NVDIMM_DBG("Input parameter is NULL"); goto Finish; } ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } CHECK_RESULT(pNvmDimmConfigProtocol->GetDriverDebugPrintErrorLevel(pNvmDimmConfigProtocol, pErrorLevel), Finish); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } EFI_STATUS SetDriverDebugPrintVerbosity(UINT32 ErrorLevel) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; NVDIMM_ENTRY(); ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } CHECK_RESULT(pNvmDimmConfigProtocol->SetDriverDebugPrintErrorLevel(pNvmDimmConfigProtocol, ErrorLevel), Finish); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif //MDEPKG_NDEBUG #endif //OS_BUILD ipmctl-03.00.00.0485/DcpmPkg/cli/NvmDimmCli.h000066400000000000000000000022461440615110200200630ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #if defined(__LINUX__) || defined(__ESX__) #define EXE_NAME L"ipmctl" #elif defined(_MSC_VER) && defined(OS_BUILD) #define EXE_NAME L"ipmctl.exe" #else #define EXE_NAME L"ipmctl.efi" #endif #define APP_DESCRIPTION L"Command Line Interface" #define DRIVER_API_DESCRIPTION L"Driver API" extern EFI_HANDLE gNvmDimmCliHiiHandle; // // This is the generated String package data for all .UNI files. // This data array is ready to be used as input of HiiAddPackages() to // create a package list (which contains Form packages, String packages, etc). // extern unsigned char ipmctlStrings[]; extern int g_basic_commands; /** Register commands on the commands list @retval a return code from called functions **/ EFI_STATUS RegisterCommands( ); /** Register basic commands on the commands list for non-root users @retval a return code from called functions **/ EFI_STATUS RegisterNonAdminUserCommands( ); /** Print the CLI application help **/ EFI_STATUS showHelp(struct Command *pCmd); ipmctl-03.00.00.0485/DcpmPkg/cli/SetDimmCommand.c000066400000000000000000001045271440615110200207250ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "SetDimmCommand.h" #include #include #include #include #include #include #include #include #include "Common.h" CONST CHAR16 *pPoisonMemoryTypeStr[POISON_MEMORY_TYPE_COUNT] = { L"MemoryMode", L"AppDirect", L"NotDefined", L"PatrolScrub" }; /** Command syntax definition **/ struct Command SetDimmCommand = { SET_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {FORCE_OPTION_SHORT, FORCE_OPTION, L"", L"",HELP_FORCE_DETAILS_TEXT, FALSE, ValueEmpty}, #ifndef OS_BUILD {L"", SOURCE_OPTION, L"", SOURCE_OPTION_HELP, L"Source of the Passphrase file",FALSE, ValueRequired}, {L"", MASTER_OPTION, L"", L"",L"Set Master Passphrase", FALSE, ValueEmpty}, #endif {L"", DEFAULT_OPTION, L"", L"",L"Set Default Settings", FALSE, ValueEmpty} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, {{DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, TRUE, ValueOptional}}, //!< targets { {CLEAR_ERROR_INJ_PROPERTY, L"", PROPERTY_VALUE_1, FALSE, ValueRequired}, {TEMPERATURE_INJ_PROPERTY, L"", HELP_TEXT_VALUE, FALSE, ValueRequired }, {POISON_INJ_PROPERTY, L"", HELP_TEXT_VALUE, FALSE, ValueRequired }, {POISON_TYPE_INJ_PROPERTY, L"", HELP_TEXT_VALUE, FALSE, ValueRequired}, {PACKAGE_SPARING_INJ_PROPERTY, L"", PROPERTY_VALUE_1, FALSE, ValueRequired }, {PERCENTAGE_REMAINING_INJ_PROPERTY, L"", HELP_TEXT_PERCENT, FALSE, ValueRequired}, {FATAL_MEDIA_ERROR_INJ_PROPERTY, L"", PROPERTY_VALUE_1, FALSE, ValueRequired}, {DIRTY_SHUTDOWN_ERROR_INJ_PROPERTY, L"", PROPERTY_VALUE_1, FALSE, ValueRequired}, #ifndef OS_BUILD {LOCKSTATE_PROPERTY, L"", LOCKSTATE_VALUE_DISABLED L"|" LOCKSTATE_VALUE_UNLOCKED L"|" LOCKSTATE_VALUE_FROZEN, FALSE, ValueRequired}, {PASSPHRASE_PROPERTY, L"", HELP_TEXT_STRING, FALSE, ValueOptional}, {NEWPASSPHRASE_PROPERTY, L"", HELP_TEXT_STRING, FALSE, ValueOptional}, {CONFIRMPASSPHRASE_PROPERTY, L"", HELP_TEXT_STRING, FALSE, ValueOptional}, #endif {AVG_PWR_REPORTING_TIME_CONSTANT, L"", HELP_TEXT_AVG_PWR_REPORTING_TIME_CONSTANT_PROPERTY, FALSE, ValueRequired} }, //!< properties #ifndef OS_BUILD L"Set properties of one or more " PMEM_MODULES_STR L", such as device security and modify device.", //!< help #else L"Set properties of one or more " PMEM_MODULES_STR L", such as modify device.", //!< help #endif SetDimm, TRUE }; CHAR16* GetCorrectClearMessageBasedOnProperty(UINT16 ErrorInjectType) { CHAR16 *ClearOutputPropertyString = NULL; switch(ErrorInjectType) { case ERROR_INJ_POISON: ClearOutputPropertyString = CLI_INFO_CLEAR_POISON_INJECT_ERROR; break; case ERROR_INJ_TEMPERATURE: ClearOutputPropertyString = CLI_INFO_CLEAR_TEMPERATURE_INJECT_ERROR; break; case ERROR_INJ_PACKAGE_SPARING: ClearOutputPropertyString = CLI_INFO_CLEAR_PACKAGE_SPARING_INJECT_ERROR; break; case ERROR_INJ_PERCENTAGE_REMAINING: ClearOutputPropertyString = CLI_INFO_CLEAR_PERCENTAGE_REMAINING_INJECT_ERROR; break; case ERROR_INJ_FATAL_MEDIA_ERR: ClearOutputPropertyString = CLI_INFO_CLEAR_FATAL_MEDIA_ERROR_INJECT_ERROR; break; case ERROR_INJ_DIRTY_SHUTDOWN: ClearOutputPropertyString = CLI_INFO_CLEAR_DIRTY_SHUT_DOWN_INJECT_ERROR; break; } return ClearOutputPropertyString; } /** Execute the set dimm command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS SetDimm( IN struct Command *pCmd ) { EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; CHAR16 *pLockStatePropertyValue = NULL; CHAR16 *pPassphrase = NULL; CHAR16 *pNewPassphrase = NULL; CHAR16 *pConfirmPassphrase = NULL; CHAR16 *pPassphraseStatic = NULL; CHAR16 *pNewPassphraseStatic = NULL; CHAR16 *pConfirmPassphraseStatic = NULL; CHAR16 *pAvgPowerReportingTimeConstantValue = NULL; BOOLEAN IsNumber = FALSE; UINT64 ParsedNumber = 0; UINT32 *pAvgPowerReportingTimeConstant = NULL; CHAR16 *pTargetValue = NULL; CHAR16 *pLoadUserPath = NULL; CHAR16 *pLoadFilePath = NULL; CHAR16 *pErrorMessage = NULL; UINT16 SecurityOperation = SECURITY_OPERATION_UNDEFINED; UINT16 *pDimmIds = NULL; UINT32 DimmHandle = 0; UINT32 DimmIndex = 0; UINT32 DimmIdsCount = 0; UINT32 Index = 0; EFI_DEVICE_PATH_PROTOCOL *pDevicePathProtocol = NULL; COMMAND_STATUS *pCommandStatus = NULL; CHAR16 *pCommandStatusMessage = NULL; CHAR16 *pCommandStatusPreposition = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; BOOLEAN ActionSpecified = FALSE; BOOLEAN OneOfPassphrasesIsEmpty = FALSE; BOOLEAN OneOfPassphrasesIsNotEmpty = FALSE; BOOLEAN LockStateFrozen = FALSE; BOOLEAN Force = FALSE; BOOLEAN Confirmation = FALSE; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; PRINT_CONTEXT *pPrinterCtx = NULL; BOOLEAN MasterOptionSpecified = FALSE; BOOLEAN DefaultOptionSpecified = FALSE; /*Inject error*/ CHAR16 *pTemperature = NULL; CHAR16 *pPoisonAddress = NULL; CHAR16 *pPoisonType = NULL; CHAR16 *pPackageSparing = NULL; CHAR16 *pPercentageRemaining = NULL; CHAR16 *pFatalMediaError = NULL; CHAR16 *pDirtyShutDown = NULL; CHAR16 *pClearErrorInj = NULL; UINT16 ErrInjectType = ERROR_INJ_TYPE_INVALID; UINT64 TemperatureValue = 0; UINT64 PoisonAddressValue = 0; UINT64 PercentageRemainingValue = 0; UINT64 PoisonTypeValue = POISON_MEMORY_TYPE_PATROLSCRUB; UINT64 ErrorInjectionTypeSet = 0; UINT64 PoisonTypeValid = 0; UINT64 ClearStatus = 0; UINT64 FatalMediaError = 0; UINT64 PackageSparing = 0; UINT64 DirtyShutDown = 0; NVDIMM_ENTRY(); ZeroMem(DimmStr, sizeof(DimmStr)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_SECURITY, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } // initialize status structure ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode) || pCommandStatus == NULL) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } // check targets if (ContainTarget(pCmd, DIMM_TARGET)) { pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetDimmIdsFromString"); goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsCount)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } } /** If no dimm IDs are specified get IDs from all dimms **/ if (DimmIdsCount == 0) { ReturnCode = GetAllManageableDimmsNumberAndId(pNvmDimmConfigProtocol, FALSE, &DimmIdsCount, &pDimmIds); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } if (DimmIdsCount == 0) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_INFO_NO_MANAGEABLE_DIMMS); goto Finish; } } /** Check master option **/ MasterOptionSpecified = containsOption(pCmd, MASTER_OPTION); /** Check default option **/ DefaultOptionSpecified = containsOption(pCmd, DEFAULT_OPTION); /** Check force option **/ if (containsOption(pCmd, FORCE_OPTION) || containsOption(pCmd, FORCE_OPTION_SHORT)) { Force = TRUE; } /** This command allows for different property sets depending on what action is to be taken. Here we check if input contains properties from different actions because they are not allowed together. **/ if (!EFI_ERROR(ContainsProperty(pCmd, AVG_PWR_REPORTING_TIME_CONSTANT))) { ActionSpecified = TRUE; } if (containsOption(pCmd, SOURCE_OPTION) || !EFI_ERROR(ContainsProperty(pCmd, PASSPHRASE_PROPERTY)) || !EFI_ERROR(ContainsProperty(pCmd, NEWPASSPHRASE_PROPERTY)) || !EFI_ERROR(ContainsProperty(pCmd, CONFIRMPASSPHRASE_PROPERTY)) || !EFI_ERROR(ContainsProperty(pCmd, LOCKSTATE_PROPERTY)) ) { if (ActionSpecified) { /** We already found a specified action, more are not allowed **/ ReturnCode = EFI_INVALID_PARAMETER; } else { /** Found specified action **/ ActionSpecified = TRUE; } } if (!EFI_ERROR(ContainsProperty(pCmd, TEMPERATURE_INJ_PROPERTY))) { if (ActionSpecified) { /** We already found a specified action, more are not allowed **/ ReturnCode = EFI_INVALID_PARAMETER; } else { /** Found specified action **/ ActionSpecified = TRUE; ErrorInjectionTypeSet = 1; } } if (!EFI_ERROR(ContainsProperty(pCmd, POISON_INJ_PROPERTY))) { if (ActionSpecified) { /** We already found a specified action, more are not allowed **/ ReturnCode = EFI_INVALID_PARAMETER; } else { /** Found specified action **/ ActionSpecified = TRUE; ErrorInjectionTypeSet = 1; } } /*If there is poison type property then there should be poison address property */ if (!EFI_ERROR(ContainsProperty(pCmd, POISON_TYPE_INJ_PROPERTY))) { GetPropertyValue(pCmd, POISON_INJ_PROPERTY, &pPoisonAddress); if ((ActionSpecified && pPoisonAddress == NULL) || !ActionSpecified) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERROR_POISON_TYPE_WITHOUT_ADDRESS); goto Finish; } } if (!EFI_ERROR(ContainsProperty(pCmd, PACKAGE_SPARING_INJ_PROPERTY))) { if (ActionSpecified) { /** We already found a specified action, more are not allowed **/ ReturnCode = EFI_INVALID_PARAMETER; } else { /** Found specified action **/ ActionSpecified = TRUE; ErrorInjectionTypeSet = 1; } } if (!EFI_ERROR(ContainsProperty(pCmd, PERCENTAGE_REMAINING_INJ_PROPERTY))) { if (ActionSpecified) { /** We already found a specified action, more are not allowed **/ ReturnCode = EFI_INVALID_PARAMETER; } else { /** Found specified action **/ ActionSpecified = TRUE; ErrorInjectionTypeSet = 1; } } if (!EFI_ERROR(ContainsProperty(pCmd, FATAL_MEDIA_ERROR_INJ_PROPERTY))) { if (ActionSpecified) { /** We already found a specified action, more are not allowed **/ ReturnCode = EFI_INVALID_PARAMETER; } else { /** Found specified action **/ ActionSpecified = TRUE; ErrorInjectionTypeSet = 1; } } if (!EFI_ERROR(ContainsProperty(pCmd, DIRTY_SHUTDOWN_ERROR_INJ_PROPERTY))) { if (ActionSpecified) { /** We already found a specified action, more are not allowed **/ ReturnCode = EFI_INVALID_PARAMETER; } else { /** Found specified action **/ ActionSpecified = TRUE; ErrorInjectionTypeSet = 1; } } /*Clear error injection requires exactly one error injection type being set*/ if (!EFI_ERROR(ContainsProperty(pCmd, CLEAR_ERROR_INJ_PROPERTY))) { if ((ActionSpecified && !ErrorInjectionTypeSet) || !ActionSpecified) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERROR_CLEAR_PROPERTY_NOT_COMBINED); goto Finish; } } /** Syntax error - mixed properties from different set -dimm commands **/ if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNSUPPORTED_COMMAND_SYNTAX); goto Finish; } /** Syntax error - no properties specified. **/ if (!ActionSpecified) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCOMPLETE_SYNTAX); goto Finish; } /** Set Security State **/ GetPropertyValue(pCmd, LOCKSTATE_PROPERTY, &pLockStatePropertyValue); GetPropertyValue(pCmd, PASSPHRASE_PROPERTY, &pPassphraseStatic); GetPropertyValue(pCmd, NEWPASSPHRASE_PROPERTY, &pNewPassphraseStatic); GetPropertyValue(pCmd, CONFIRMPASSPHRASE_PROPERTY, &pConfirmPassphraseStatic); if (MasterOptionSpecified) { // DefaultOptionSpecified = Ignore Master Passphrase enabled check only when changing the master passphrase from the default on FIS >= 3.2 PMem modules if (!AllDimmsInListHaveMasterPassphraseEnabled(pDimms, DimmCount, pDimmIds, DimmIdsCount, DefaultOptionSpecified)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_MASTER_PASSPHRASE_NOT_ENABLED); goto Finish; } } pLoadFilePath = AllocateZeroPool(OPTION_VALUE_LEN * sizeof(*pLoadFilePath)); if (pLoadFilePath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } if (pLockStatePropertyValue != NULL || pPassphraseStatic != NULL || pNewPassphraseStatic != NULL || pConfirmPassphraseStatic != NULL || containsOption(pCmd, SOURCE_OPTION)) { LockStateFrozen = (pLockStatePropertyValue != NULL && (StrICmp(pLockStatePropertyValue, LOCKSTATE_VALUE_FROZEN) == 0)); if (pLockStatePropertyValue == NULL && pPassphraseStatic == NULL && pNewPassphraseStatic != NULL && pConfirmPassphraseStatic != NULL) { // Passphrase not provided ReturnCode = CheckMasterAndDefaultOptions(pCmd, FALSE, MasterOptionSpecified, DefaultOptionSpecified); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (MasterOptionSpecified) { pPassphrase = CatSPrint(NULL, L""); SecurityOperation = SECURITY_OPERATION_CHANGE_MASTER_PASSPHRASE; pCommandStatusMessage = CatSPrint(NULL, FORMAT_STR, L"Change master passphrase"); } else { SecurityOperation = SECURITY_OPERATION_SET_PASSPHRASE; pCommandStatusMessage = CatSPrint(NULL, FORMAT_STR, L"Set passphrase"); } pCommandStatusPreposition = CatSPrint(NULL, FORMAT_STR, L" on"); } else if (pLockStatePropertyValue == NULL && pPassphraseStatic != NULL && pNewPassphraseStatic != NULL && pConfirmPassphraseStatic != NULL) { // Passphrase provided ReturnCode = CheckMasterAndDefaultOptions(pCmd, TRUE, MasterOptionSpecified, DefaultOptionSpecified); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (MasterOptionSpecified) { SecurityOperation = SECURITY_OPERATION_CHANGE_MASTER_PASSPHRASE; pCommandStatusMessage = CatSPrint(NULL, FORMAT_STR, L"Change master passphrase"); } else { SecurityOperation = SECURITY_OPERATION_CHANGE_PASSPHRASE; pCommandStatusMessage = CatSPrint(NULL, FORMAT_STR, L"Change passphrase"); } pCommandStatusPreposition = CatSPrint(NULL, FORMAT_STR, L" on"); } else if (pLockStatePropertyValue != NULL && pNewPassphraseStatic != NULL && ((StrICmp(pLockStatePropertyValue, LOCKSTATE_VALUE_DISABLED) == 0) || (StrICmp(pLockStatePropertyValue, LOCKSTATE_VALUE_UNLOCKED) == 0) || (StrICmp(pLockStatePropertyValue, LOCKSTATE_VALUE_FROZEN) == 0))) { pErrorMessage = CatSPrint(NULL, CLI_PARSER_ERR_UNEXPECTED_TOKEN, L"NewPassphrase="); ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR, pErrorMessage); goto Finish; } else if (pLockStatePropertyValue != NULL && pConfirmPassphraseStatic != NULL && ((StrICmp(pLockStatePropertyValue, LOCKSTATE_VALUE_DISABLED) == 0) || (StrICmp(pLockStatePropertyValue, LOCKSTATE_VALUE_UNLOCKED) == 0) || (StrICmp(pLockStatePropertyValue, LOCKSTATE_VALUE_FROZEN) == 0))) { pErrorMessage = CatSPrint(NULL, CLI_PARSER_ERR_UNEXPECTED_TOKEN, L"ConfirmPassphrase="); ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR, pErrorMessage); goto Finish; } else if (pLockStatePropertyValue != NULL && pPassphraseStatic != NULL && (StrICmp(pLockStatePropertyValue, LOCKSTATE_VALUE_DISABLED) == 0)) { SecurityOperation = SECURITY_OPERATION_DISABLE_PASSPHRASE; pCommandStatusMessage = CatSPrint(NULL, FORMAT_STR, L"Remove passphrase"); pCommandStatusPreposition = CatSPrint(NULL, FORMAT_STR, L" from"); } else if (pLockStatePropertyValue != NULL && pPassphraseStatic != NULL && (StrICmp(pLockStatePropertyValue, LOCKSTATE_VALUE_UNLOCKED) == 0)) { SecurityOperation = SECURITY_OPERATION_UNLOCK_DEVICE; pCommandStatusMessage = CatSPrint(NULL, FORMAT_STR, L"Unlock"); pCommandStatusPreposition = CatSPrint(NULL, FORMAT_STR, L""); } else if (LockStateFrozen) { SecurityOperation = SECURITY_OPERATION_FREEZE_DEVICE; pCommandStatusMessage = CatSPrint(NULL, FORMAT_STR, L"Freeze lock"); pCommandStatusPreposition = CatSPrint(NULL, FORMAT_STR, L""); } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCOMPLETE_SYNTAX); goto Finish; } OneOfPassphrasesIsEmpty = ((pPassphraseStatic != NULL && StrCmp(pPassphraseStatic, L"") == 0) || (pNewPassphraseStatic != NULL && StrCmp(pNewPassphraseStatic, L"") == 0) || (pConfirmPassphraseStatic != NULL && StrCmp(pConfirmPassphraseStatic, L"") == 0)); OneOfPassphrasesIsNotEmpty = ((pPassphraseStatic != NULL && StrCmp(pPassphraseStatic, L"") != 0) || (pNewPassphraseStatic != NULL && StrCmp(pNewPassphraseStatic, L"") != 0) || (pConfirmPassphraseStatic != NULL && StrCmp(pConfirmPassphraseStatic, L"") != 0)); // Check -source option if (containsOption(pCmd, SOURCE_OPTION) && !LockStateFrozen) { if (OneOfPassphrasesIsNotEmpty) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNSUPPORTED_COMMAND_SYNTAX); goto Finish; } pLoadUserPath = getOptionValue(pCmd, SOURCE_OPTION); if (pLoadUserPath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Could not get -source value. Out of memory"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = GetDeviceAndFilePath(pLoadUserPath, pLoadFilePath, &pDevicePathProtocol); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get file path (" FORMAT_EFI_STATUS ")", ReturnCode); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_WRONG_FILE_PATH); goto Finish; } ReturnCode = ParseSourcePassFile(pCmd, pLoadFilePath, pDevicePathProtocol, &pPassphrase, &pNewPassphrase); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("ParseSourcePassFile failed (" FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } // Check if required passwords have been found in the file if ((pPassphrase == NULL && pPassphraseStatic != NULL) || (pNewPassphrase == NULL && pNewPassphraseStatic != NULL)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_WRONG_FILE_DATA); goto Finish; // NewPassphrase and ConfirmPassphrase occur together } else if (pNewPassphrase != NULL) { pConfirmPassphrase = CatSPrint(NULL, FORMAT_STR, pNewPassphrase); } // Check prompts } else { if (OneOfPassphrasesIsEmpty && OneOfPassphrasesIsNotEmpty) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNSUPPORTED_COMMAND_SYNTAX); goto Finish; } // Check prompt request Passphrase if (pPassphraseStatic != NULL && StrCmp(pPassphraseStatic, L"") == 0) { ReturnCode = PromptedInput(L"Enter passphrase:\n", FALSE, FALSE, &pPassphrase); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_PROMPT_INVALID); NVDIMM_DBG("Failed on PromptedInput"); goto Finish; } } else if (pPassphraseStatic != NULL) { pPassphrase = CatSPrint(NULL, FORMAT_STR, pPassphraseStatic); } // Check prompt request NewPassphrase if (pNewPassphraseStatic != NULL && StrCmp(pNewPassphraseStatic, L"") == 0) { ReturnCode = PromptedInput(L"Enter new passphrase:", FALSE, FALSE, &pNewPassphrase); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_PROMPT_INVALID); NVDIMM_DBG("Failed on PromptedInput"); goto Finish; } } else if (pNewPassphraseStatic != NULL) { pNewPassphrase = CatSPrint(NULL, FORMAT_STR, pNewPassphraseStatic); } // Check prompt request ConfirmPassphrase if (pConfirmPassphraseStatic != NULL && StrCmp(pConfirmPassphraseStatic, L"") == 0) { ReturnCode = PromptedInput(L"Confirm passphrase:", FALSE, FALSE, &pConfirmPassphrase); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_PROMPT_INVALID); NVDIMM_DBG("Failed on PromptedInput"); goto Finish; } } else if (pConfirmPassphraseStatic != NULL) { pConfirmPassphrase = CatSPrint(NULL, FORMAT_STR, pConfirmPassphraseStatic); } } if (pLockStatePropertyValue == NULL && pNewPassphrase != NULL && pConfirmPassphrase != NULL) { if ((StrCmp(pNewPassphrase, pConfirmPassphrase) != 0)) { ResetCmdStatus(pCommandStatus, NVM_ERR_PASSPHRASES_DO_NOT_MATCH); goto FinishCommandStatusSet; } } ReturnCode = pNvmDimmConfigProtocol->SetSecurityState(pNvmDimmConfigProtocol, pDimmIds, DimmIdsCount, SecurityOperation, pPassphrase, pNewPassphrase, pCommandStatus); goto FinishCommandStatusSet; } /** Set AveragePowerReportingTimeConstant **/ GetPropertyValue(pCmd, AVG_PWR_REPORTING_TIME_CONSTANT, &pAvgPowerReportingTimeConstantValue); if (pAvgPowerReportingTimeConstantValue) { // If average power reporting time constant property exists, check its validity IsNumber = GetU64FromString(pAvgPowerReportingTimeConstantValue, &ParsedNumber); if (!IsNumber) { NVDIMM_WARN("Average power reporting time constant value is not a number"); ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_INCORRECT_VALUE_FOR_PROPERTY_AVG_PWR_REPORTING_TIME_CONSTANT); goto Finish; } else if (ParsedNumber > AVG_PWR_REPORTING_TIME_CONSTANT_MAX) { NVDIMM_WARN("Average power reporting time constant value %d is greater than maximum %d", ParsedNumber, AVG_PWR_REPORTING_TIME_CONSTANT_MAX); ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_INCORRECT_VALUE_FOR_PROPERTY_AVG_PWR_REPORTING_TIME_CONSTANT); goto Finish; } pAvgPowerReportingTimeConstant = AllocateZeroPool(sizeof(UINT32)); *pAvgPowerReportingTimeConstant = (UINT32)ParsedNumber; pCommandStatusMessage = CatSPrint(NULL, FORMAT_STR, L"Modify"); pCommandStatusPreposition = CatSPrint(NULL, FORMAT_STR, L""); if (!Force) { for (Index = 0; Index < DimmIdsCount; Index++) { ReturnCode = GetDimmHandleByPid(pDimmIds[Index], pDimms, DimmCount, &DimmHandle, &DimmIndex); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } ReturnCode = GetPreferredDimmIdAsString(DimmHandle, pDimms[DimmIndex].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, L"Modifying device settings on " PMEM_MODULE_STR L" (" FORMAT_STR L").", DimmStr); ReturnCode = PromptYesNo(&Confirmation); if (!EFI_ERROR(ReturnCode) && Confirmation) { ReturnCode = pNvmDimmConfigProtocol->SetOptionalConfigurationDataPolicy(pNvmDimmConfigProtocol, &pDimmIds[Index], 1, NULL, pAvgPowerReportingTimeConstant, pCommandStatus); } else { PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, L"Skipping modify device settings on " PMEM_MODULE_STR L" (" FORMAT_STR L")\n", DimmStr); continue; } } } else { ReturnCode = pNvmDimmConfigProtocol->SetOptionalConfigurationDataPolicy(pNvmDimmConfigProtocol, pDimmIds, DimmIdsCount, NULL, pAvgPowerReportingTimeConstant, pCommandStatus); goto FinishCommandStatusSet; } } GetPropertyValue(pCmd, TEMPERATURE_INJ_PROPERTY, &pTemperature); GetPropertyValue(pCmd, POISON_INJ_PROPERTY, &pPoisonAddress); GetPropertyValue(pCmd, POISON_TYPE_INJ_PROPERTY, &pPoisonType); GetPropertyValue(pCmd, PACKAGE_SPARING_INJ_PROPERTY, &pPackageSparing); GetPropertyValue(pCmd, PERCENTAGE_REMAINING_INJ_PROPERTY, &pPercentageRemaining); GetPropertyValue(pCmd, FATAL_MEDIA_ERROR_INJ_PROPERTY, &pFatalMediaError); GetPropertyValue(pCmd, DIRTY_SHUTDOWN_ERROR_INJ_PROPERTY, &pDirtyShutDown); GetPropertyValue(pCmd, CLEAR_ERROR_INJ_PROPERTY, &pClearErrorInj); // Implementation notes: // Assumes only one property can be set. // pCommandStatusMessage seems to be a print wrapper around the pCommandStatus that // is finally returned from InjectError() at the bottom. // If there is an error with converting to an integer, we don't want to print // out the command status message as InjectError() is never called. Go to finish // with an invalid parameter /** Inject error Temperature **/ if (pTemperature != NULL) { pCommandStatusMessage = CatSPrint(NULL, CLI_INFO_TEMPERATURE_INJECT_ERROR); pCommandStatusPreposition = CatSPrint(NULL, CLI_INFO_ON); ErrInjectType = ERROR_INJ_TEMPERATURE; ReturnCode = GetU64FromString(pTemperature, &TemperatureValue); if (!ReturnCode) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNSUPPORTED_COMMAND_SYNTAX); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } /** Inject Poison Error **/ if (pPoisonAddress != NULL) { pCommandStatusMessage = CatSPrint(NULL, CLI_INFO_POISON_INJECT_ERROR, pPoisonAddress); pCommandStatusPreposition = CatSPrint(NULL, CLI_INFO_ON); ErrInjectType = ERROR_INJ_POISON; ReturnCode = IsHexValue(pPoisonAddress, FALSE); // ReturnCode here indicates if it is hex value if (!ReturnCode) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR FORMAT_STR_SINGLE_QUOTE FORMAT_STR L" 'Poison'\n", CLI_SYNTAX_ERROR, pPoisonAddress, CLI_ERR_INCORRECT_VALUE_FOR_PROPERTY); goto Finish; } ReturnCode = GetU64FromString(pPoisonAddress, &PoisonAddressValue); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR FORMAT_STR_SINGLE_QUOTE FORMAT_STR L" 'Poison'\n", CLI_SYNTAX_ERROR, pPoisonAddress, CLI_ERR_INCORRECT_VALUE_FOR_PROPERTY); goto Finish; } } /** Inject Poison Type Error **/ if (pPoisonType != NULL) { /** Check if poison MemoryType is valid **/ for (Index = 0; Index < POISON_MEMORY_TYPE_COUNT; ++Index) { if (0 == StrICmp(pPoisonType, pPoisonMemoryTypeStr[Index])) { PoisonTypeValid = 1; PoisonTypeValue = (UINT8)Index + 1; } } if (!PoisonTypeValid) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_INCORRECT_VALUE_POISON_TYPE); goto Finish; } } /** PACKAGE_SPARING_INJ_PROPERTY **/ if (pPackageSparing != NULL) { pCommandStatusMessage = CatSPrint(NULL, CLI_INFO_PACKAGE_SPARING_INJECT_ERROR); pCommandStatusPreposition = CatSPrint(NULL, CLI_INFO_ON); ReturnCode = GetU64FromString(pPackageSparing, (UINT64 *)&PackageSparing); if (!ReturnCode || 1 != PackageSparing) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR FORMAT_STR_SINGLE_QUOTE FORMAT_STR L" 'PackageSparing'\n", CLI_SYNTAX_ERROR, pPackageSparing, CLI_ERR_INCORRECT_VALUE_FOR_PROPERTY); goto Finish; } ErrInjectType = ERROR_INJ_PACKAGE_SPARING; } /** Percentage remaining property **/ if (pPercentageRemaining != NULL) { pCommandStatusMessage = CatSPrint(NULL, CLI_INFO_PERCENTAGE_REMAINING_INJECT_ERROR); pCommandStatusPreposition = CatSPrint(NULL, CLI_INFO_ON); ReturnCode = GetU64FromString(pPercentageRemaining, &PercentageRemainingValue); if (!ReturnCode || PercentageRemainingValue > 100) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR FORMAT_STR_SINGLE_QUOTE FORMAT_STR L" 'PercentageRemaining'\n", CLI_SYNTAX_ERROR, pPercentageRemaining, CLI_ERR_INCORRECT_VALUE_FOR_PROPERTY); goto Finish; } ErrInjectType = ERROR_INJ_PERCENTAGE_REMAINING; } /** Fatal Media Error Inj property **/ if (pFatalMediaError != NULL) { #if defined( _MSC_VER ) && defined( OS_BUILD ) // Windows // We can't recover from an injected fatal media error in Windows // because nvmdimm.sys requires GetCommandEffectLog to work and that // command requires the media to be up because it uses the large payload. Therefore // redirect people to use the Microsoft tools for doing this that ignores the // lack of a Command Effect Log for the dimm. Print(CLI_ERR_INJECT_FATAL_ERROR_UNSUPPORTED_ON_OS); ReturnCode = EFI_UNSUPPORTED; goto Finish; #endif // _MSC_VER pCommandStatusMessage = CatSPrint(NULL, CLI_INFO_FATAL_MEDIA_ERROR_INJECT_ERROR); pCommandStatusPreposition = CatSPrint(NULL, CLI_INFO_ON); ReturnCode = GetU64FromString(pFatalMediaError, &FatalMediaError); if (!ReturnCode || 1 != FatalMediaError) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR FORMAT_STR_SINGLE_QUOTE FORMAT_STR L" 'FatalMediaError'\n", CLI_SYNTAX_ERROR, pFatalMediaError, CLI_ERR_INCORRECT_VALUE_FOR_PROPERTY); goto Finish; } ErrInjectType = ERROR_INJ_FATAL_MEDIA_ERR; } /** Dirty shutdown error injection **/ if (pDirtyShutDown != NULL) { pCommandStatusMessage = CatSPrint(NULL, CLI_INFO_DIRTY_SHUT_DOWN_INJECT_ERROR); pCommandStatusPreposition = CatSPrint(NULL, CLI_INFO_ON); ReturnCode = GetU64FromString(pDirtyShutDown, &DirtyShutDown); if (!ReturnCode || 1 != DirtyShutDown) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR FORMAT_STR_SINGLE_QUOTE FORMAT_STR L" 'DirtyShutDown'\n", CLI_SYNTAX_ERROR, pDirtyShutDown, CLI_ERR_INCORRECT_VALUE_FOR_PROPERTY); goto Finish; } ErrInjectType = ERROR_INJ_DIRTY_SHUTDOWN; } /** Clear error injection **/ if (pClearErrorInj != NULL) { pCommandStatusMessage = CatSPrint(NULL, GetCorrectClearMessageBasedOnProperty(ErrInjectType), pPoisonAddress); pCommandStatusPreposition = CatSPrint(NULL, CLI_INFO_ON); ReturnCode = GetU64FromString(pClearErrorInj, &ClearStatus); if (!ReturnCode || 1 != ClearStatus) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR FORMAT_STR_SINGLE_QUOTE FORMAT_STR L" 'Clear'\n", CLI_SYNTAX_ERROR, ClearStatus, CLI_ERR_INCORRECT_VALUE_FOR_PROPERTY); goto Finish; } } if (ErrInjectType != ERROR_INJ_TYPE_INVALID) { ReturnCode = pNvmDimmConfigProtocol->InjectError(pNvmDimmConfigProtocol, pDimmIds, DimmIdsCount, (UINT8)ErrInjectType, (UINT8)ClearStatus, &TemperatureValue, &PoisonAddressValue, (UINT8 *)&PoisonTypeValue, (UINT8 *)&PercentageRemainingValue, pCommandStatus); } FinishCommandStatusSet: ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, pCommandStatusMessage, pCommandStatusPreposition, pCommandStatus); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FreeCommandStatus(&pCommandStatus); CleanUnicodeStringMemory(pLockStatePropertyValue); CleanUnicodeStringMemory(pPassphraseStatic); CleanUnicodeStringMemory(pNewPassphraseStatic); CleanUnicodeStringMemory(pConfirmPassphraseStatic); CleanUnicodeStringMemory(pPassphrase); CleanUnicodeStringMemory(pNewPassphrase); CleanUnicodeStringMemory(pConfirmPassphrase); FREE_POOL_SAFE(pPassphrase); FREE_POOL_SAFE(pNewPassphrase); FREE_POOL_SAFE(pConfirmPassphrase); FREE_POOL_SAFE(pLoadFilePath); FREE_POOL_SAFE(pLoadUserPath); FREE_POOL_SAFE(pErrorMessage); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pCommandStatusMessage); FREE_POOL_SAFE(pCommandStatusPreposition); FREE_POOL_SAFE(pAvgPowerReportingTimeConstant); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the set dimm command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterSetDimmCommand( ) { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&SetDimmCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/SetDimmCommand.h000066400000000000000000000024641440615110200207270ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SET_DIMM_COMMAND_ #define _SET_DIMM_COMMAND_ #include #include "CommandParser.h" #include #define FW_LOG_LEVELS_COUNT 4 #define FW_LOG_LEVELS_INVALID_LEVEL 255 #define POISON_MEMORY_TYPE_COUNT 4 #define POISON_MEMORY_TYPE_MEMORYMODE 0x01 // currently allocated in Memory mode #define POISON_MEMORY_TYPE_APPDIRECT 0x02 // currently allocated in AppDirect #define POISON_MEMORY_TYPE_PATROLSCRUB 0x04// simulating an error found during a patrol scrub operation /** Register the set dimm command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterSetDimmCommand(); /** Execute the set dimm command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS SetDimm( IN struct Command *pCmd ); /** Print detailed information on root cause of failure @param[in] FwReturnCode is a FW status code returned by PassThru protocol **/ void PrintFailureDetails( IN UINT8 FwReturnCode ); #endif /** _SET_DIMM_COMMAND_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/SetPreferencesCommand.c000066400000000000000000000371021440615110200222720ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "Debug.h" #include "Types.h" #include "NvmInterface.h" #include "CommandParser.h" #include "SetPreferencesCommand.h" #include "Common.h" #include "Convert.h" #include "Utility.h" #include #ifdef OS_BUILD #include #else extern EFI_RUNTIME_SERVICES *gRT; #endif /** Local definitions **/ #define MIN_LOG_LEVEL_VALUE 0 #define MAX_LOG_LEVEL_VALUE 4 /** Command syntax definition **/ struct Command SetPreferencesCommand = { SET_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #else {L"", L"", L"", L"", L"", FALSE, ValueOptional} #endif }, { //!< targets {PREFERENCES_TARGET, L"", L"", TRUE, ValueEmpty}, }, { //!< properties {CLI_DEFAULT_DIMM_ID_PROPERTY, L"", PROPERTY_VALUE_HANDLE L"|" PROPERTY_VALUE_UID, FALSE, ValueRequired}, {CLI_DEFAULT_SIZE_PROPERTY, L"", HELP_TEXT_DEFAULT_SIZE, FALSE, ValueRequired}, {APP_DIRECT_SETTINGS_PROPERTY, L"", HELP_TEXT_APPDIRECT_SETTINGS, FALSE, ValueRequired}, #ifdef OS_BUILD {DBG_LOG_LEVEL, L"", HELP_DBG_LOG_LEVEL, FALSE, ValueRequired}, #endif }, L"Set user preferences.", //!< help SetPreferences, TRUE }; STATIC UINT8 GetBitFieldForInterleaveChannelSize( IN CHAR16 *pStringValue ) { UINT8 BitField = CHANNEL_INTERLEAVE_SIZE_INVALID; if (pStringValue == NULL) { return BitField; } if (StrICmp(pStringValue, L"64B") == 0) { BitField = CHANNEL_INTERLEAVE_SIZE_64B; } else if (StrICmp(pStringValue, L"128B") == 0) { BitField = CHANNEL_INTERLEAVE_SIZE_128B; } else if (StrICmp(pStringValue, L"256B") == 0) { BitField = CHANNEL_INTERLEAVE_SIZE_256B; } else if (StrICmp(pStringValue, L"4KB") == 0) { BitField = CHANNEL_INTERLEAVE_SIZE_4KB; } else if (StrICmp(pStringValue, L"1GB") == 0) { BitField = CHANNEL_INTERLEAVE_SIZE_1GB; } return BitField; } STATIC UINT8 GetBitFieldForInterleaveImcSize( IN CHAR16 *pStringValue ) { UINT8 BitField = IMC_INTERLEAVE_SIZE_INVALID; if (pStringValue == NULL) { return BitField; } if (StrICmp(pStringValue, L"64B") == 0) { BitField = IMC_INTERLEAVE_SIZE_64B; } else if (StrICmp(pStringValue, L"128B") == 0) { BitField = IMC_INTERLEAVE_SIZE_128B; } else if (StrICmp(pStringValue, L"256B") == 0) { BitField = IMC_INTERLEAVE_SIZE_256B; } else if (StrICmp(pStringValue, L"4KB") == 0) { BitField = IMC_INTERLEAVE_SIZE_4KB; } else if (StrICmp(pStringValue, L"1GB") == 0) { BitField = IMC_INTERLEAVE_SIZE_1GB; } return BitField; } STATIC EFI_STATUS GetAppDirectSettingsBitFields( IN CHAR16 *pAppDirectSettings, OUT UINT8 *pImcBitField, OUT UINT8 *pChannelBitField ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 **ppSplitSettings = NULL; UINT32 NumOfElements = 0; if (pAppDirectSettings == NULL || pImcBitField == NULL || pChannelBitField == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // default values *pImcBitField = DEFAULT_IMC_INTERLEAVE_SIZE; *pChannelBitField = DEFAULT_CHANNEL_INTERLEAVE_SIZE; ppSplitSettings = StrSplit(pAppDirectSettings, L'_', &NumOfElements); if (ppSplitSettings == NULL || NumOfElements != 2) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pImcBitField = GetBitFieldForInterleaveImcSize(ppSplitSettings[0]); *pChannelBitField = GetBitFieldForInterleaveChannelSize(ppSplitSettings[1]); if (*pImcBitField == IMC_INTERLEAVE_SIZE_INVALID || *pChannelBitField == CHANNEL_INTERLEAVE_SIZE_INVALID) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } Finish: if (ppSplitSettings != NULL) { FreeStringArray(ppSplitSettings, NumOfElements); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #ifdef OS_BUILD EFI_STATUS ValidateAndConvertInput(CHAR16 *InputString, UINT64 MinVal, UINT64 MaxValue, UINT64 *IntegerEq) { EFI_STATUS rc = EFI_INVALID_PARAMETER; if (!GetU64FromString(InputString, IntegerEq)) { goto Finish; } if (*IntegerEq > MaxValue || *IntegerEq < MinVal) { goto Finish; } rc = EFI_SUCCESS; Finish: return rc; } EFI_STATUS SetPreferenceStr(IN struct Command *pCmd, IN CONST CHAR16 * pName, IN CONST CHAR8 *pIfNotFoundWarning, IN UINT64 MinVal, IN UINT64 MaxValue, OUT COMMAND_STATUS* pCommandStatus) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 *pTypeValue = NULL; UINT64 IntegerValue; if ((ReturnCode = ContainsProperty(pCmd, pName)) != EFI_NOT_FOUND) { if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); SetObjStatus(pCommandStatus, 0, NULL, 0, NVM_ERR_OPERATION_FAILED, ObjectTypeUnknown); goto Finish; } ReturnCode = GetPropertyValue(pCmd, pName, &pTypeValue); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); SetObjStatus(pCommandStatus, 0, NULL, 0, NVM_ERR_OPERATION_FAILED, ObjectTypeUnknown); goto Finish; } ReturnCode = ValidateAndConvertInput(pTypeValue, MinVal, MaxValue, &IntegerValue); if (EFI_ERROR(ReturnCode) || ((StrCmp(pName, DBG_LOG_LEVEL) == 0) && IntegerValue > 4)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_SET_PREFERENCE_ERROR, pName, pTypeValue, ReturnCode, PROPERTY_ERROR_INVALID_OUT_OF_RANGE); SetObjStatus(pCommandStatus, 0, NULL, 0, NVM_ERR_INVALID_PARAMETER, ObjectTypeUnknown); goto Finish; } else { if (ReturnCode == EFI_SUCCESS) { ReturnCode = SET_STR_VARIABLE_NV(pName, gNvmDimmVariableGuid, pTypeValue); if (!EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_SET_PREFERENCE_SUCCESS, pName, pTypeValue); SetObjStatus(pCommandStatus, 0, NULL, 0, NVM_SUCCESS, ObjectTypeUnknown); } else { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_SET_PREFERENCE_ERROR, pName, pTypeValue, ReturnCode, PROPERTY_ERROR_SET_FAILED_UNKNOWN); SetObjStatus(pCommandStatus, 0, NULL, 0, NVM_ERR_OPERATION_FAILED, ObjectTypeUnknown); } } } } Finish: return ReturnCode; } #endif /** Execute the Set Preferences command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action @retval EFI_NOT_FOUND Cli display preferences could not be retrieved successfully **/ EFI_STATUS SetPreferences( IN struct Command *pCmd ) { EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; COMMAND_STATUS *pCommandStatus = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TempReturnCode = EFI_SUCCESS; DRIVER_PREFERENCES DriverPreferences; DISPLAY_PREFERENCES DisplayPreferences; UINT8 Index = 0; CHAR16 *pTypeValue = NULL; UINTN VariableSize = 0; PRINT_CONTEXT *pPrinterCtx = NULL; UINT16 PropertyCnt = 0; NVDIMM_ENTRY(); ZeroMem(&DriverPreferences, sizeof(DriverPreferences)); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } //verify we have at least one preference to set. ReturnCode = GetPropertyCount(pCmd, &PropertyCnt); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } else if (0 == PropertyCnt) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("Preference not specified\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCOMPLETE_SYNTAX); goto Finish; } /** Need NvmDimmConfigProtocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Retrieve current settings ReturnCode = pNvmDimmConfigProtocol->GetDriverPreferences(pNvmDimmConfigProtocol, &DriverPreferences, pCommandStatus); if (EFI_ERROR(ReturnCode)) { ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, L"Set preferences", L" on", pCommandStatus); goto Finish; } ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } if ((TempReturnCode = ContainsProperty(pCmd, CLI_DEFAULT_DIMM_ID_PROPERTY)) != EFI_NOT_FOUND) { if (EFI_ERROR(TempReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); KEEP_ERROR(ReturnCode, TempReturnCode); goto Finish; } TempReturnCode = GetPropertyValue(pCmd, CLI_DEFAULT_DIMM_ID_PROPERTY, &pTypeValue); if (EFI_ERROR(TempReturnCode)) { KEEP_ERROR(ReturnCode, EFI_INVALID_PARAMETER); NVDIMM_WARN("Default DimmID Type not provided"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_SET_PREFERENCE_ERROR, CLI_DEFAULT_DIMM_ID_PROPERTY, L"", ReturnCode, PROPERTY_ERROR_DEFAULT_DIMM_NOT_PROVIDED); } else if ((Index = GetDimmIDIndex(pTypeValue)) >= DISPLAY_DIMM_ID_MAX_SIZE) { KEEP_ERROR(ReturnCode, EFI_INVALID_PARAMETER); NVDIMM_WARN("Incorrect default DimmID type"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_SET_PREFERENCE_ERROR, CLI_DEFAULT_DIMM_ID_PROPERTY, pTypeValue, ReturnCode, PROPERTY_ERROR_INCORRECT_DEFAULT_DIMM_TYPE); } else { DisplayPreferences.DimmIdentifier = Index; VariableSize = sizeof(DisplayPreferences.DimmIdentifier); TempReturnCode = SET_VARIABLE_NV( CLI_DEFAULT_DIMM_ID_PROPERTY, gNvmDimmVariableGuid, VariableSize, &DisplayPreferences.DimmIdentifier); if (!EFI_ERROR(TempReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_SET_PREFERENCE_SUCCESS, CLI_DEFAULT_DIMM_ID_PROPERTY, pTypeValue); } else { KEEP_ERROR(ReturnCode,TempReturnCode); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_SET_PREFERENCE_ERROR, CLI_DEFAULT_DIMM_ID_PROPERTY, pTypeValue, ReturnCode, PROPERTY_ERROR_UNKNOWN); } } } if ((TempReturnCode = ContainsProperty(pCmd, CLI_DEFAULT_SIZE_PROPERTY)) != EFI_NOT_FOUND) { if (EFI_ERROR(TempReturnCode)) { KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } TempReturnCode = GetPropertyValue(pCmd, CLI_DEFAULT_SIZE_PROPERTY, &pTypeValue); if (EFI_ERROR(TempReturnCode)) { KEEP_ERROR(ReturnCode, EFI_INVALID_PARAMETER); NVDIMM_WARN("Display default size type not provided"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_SET_PREFERENCE_ERROR, CLI_DEFAULT_SIZE_PROPERTY, L"", ReturnCode, PROPERTY_ERROR_DISPLAY_DEFAULT_NOT_PROVIDED); } else if ((Index = GetDisplaySizeIndex(pTypeValue)) >= DISPLAY_SIZE_MAX_SIZE) { KEEP_ERROR(ReturnCode, EFI_INVALID_PARAMETER); NVDIMM_WARN("Incorrect default size type"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_SET_PREFERENCE_ERROR, CLI_DEFAULT_SIZE_PROPERTY, pTypeValue, ReturnCode, PROPERTY_ERROR_DEFAULT_INCORRECT_SIZE_TYPE); } else { DisplayPreferences.SizeUnit = Index; VariableSize = sizeof(DisplayPreferences.SizeUnit); TempReturnCode = SET_VARIABLE_NV( CLI_DEFAULT_SIZE_PROPERTY, gNvmDimmVariableGuid, VariableSize, &DisplayPreferences.SizeUnit); if (TempReturnCode == EFI_SUCCESS) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_SET_PREFERENCE_SUCCESS, CLI_DEFAULT_SIZE_PROPERTY, pTypeValue); } else { KEEP_ERROR(ReturnCode,TempReturnCode); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_SET_PREFERENCE_ERROR, CLI_DEFAULT_SIZE_PROPERTY, pTypeValue, ReturnCode, PROPERTY_ERROR_UNKNOWN); } } } if ((TempReturnCode = ContainsProperty(pCmd, APP_DIRECT_SETTINGS_PROPERTY)) != EFI_NOT_FOUND) { if (EFI_ERROR(ReturnCode)) { KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } TempReturnCode = GetPropertyValue(pCmd, APP_DIRECT_SETTINGS_PROPERTY, &pTypeValue); if (EFI_ERROR(TempReturnCode)) { KEEP_ERROR(ReturnCode, EFI_INVALID_PARAMETER); NVDIMM_WARN("AppDirect interleave setting type not provided"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_SET_PREFERENCE_ERROR, APP_DIRECT_SETTINGS_PROPERTY, L"", ReturnCode, PROPERTY_ERROR_INTERLEAVE_TYPE_NOT_PROVIDED); } else { if (StrICmp(pTypeValue, PROPERTY_VALUE_RECOMMENDED) == 0) { DriverPreferences.ChannelInterleaving = DEFAULT_CHANNEL_INTERLEAVE_SIZE; DriverPreferences.ImcInterleaving = DEFAULT_IMC_INTERLEAVE_SIZE; } else if ((TempReturnCode = GetAppDirectSettingsBitFields(pTypeValue, &DriverPreferences.ImcInterleaving, &DriverPreferences.ChannelInterleaving)) != EFI_SUCCESS) { KEEP_ERROR(ReturnCode, EFI_INVALID_PARAMETER); NVDIMM_WARN("Incorrect AppDirect interleave setting type"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_SET_PREFERENCE_ERROR, APP_DIRECT_SETTINGS_PROPERTY, pTypeValue, ReturnCode, PROPERTY_ERROR_APPDIR_INTERLEAVE_TYPE); } if (TempReturnCode == EFI_SUCCESS) { TempReturnCode = pNvmDimmConfigProtocol->SetDriverPreferences(pNvmDimmConfigProtocol, &DriverPreferences, pCommandStatus); if (TempReturnCode == EFI_SUCCESS) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_SET_PREFERENCE_SUCCESS, APP_DIRECT_SETTINGS_PROPERTY, pTypeValue); } else { TempReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_SET_PREFERENCE_ERROR, APP_DIRECT_SETTINGS_PROPERTY, pTypeValue, ReturnCode, PROPERTY_ERROR_UNKNOWN); } } } } #ifdef OS_BUILD SetPreferenceStr(pCmd, DBG_LOG_LEVEL, "Log level setting type not provided", MIN_LOG_LEVEL_VALUE, MAX_LOG_LEVEL_VALUE, pCommandStatus); TempReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); KEEP_ERROR(ReturnCode, TempReturnCode); #endif Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FreeCommandStatus(&pCommandStatus); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the Set Preferences command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterSetPreferencesCommand( ) { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&SetPreferencesCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/SetPreferencesCommand.h000066400000000000000000000017521440615110200223010ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SET_PREFERENCES_COMMAND_ #define _SET_PREFERENCES_COMMAND_ #include #include "CommandParser.h" #define CLI_SET_PREFERENCE_SUCCESS L"Set " FORMAT_STR L"=" FORMAT_STR L": Success\n" #define CLI_SET_PREFERENCE_ERROR L"Set " FORMAT_STR L"=" FORMAT_STR L". Error: %d. " FORMAT_STR L"\n" /** Register the Set Preferences command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterSetPreferencesCommand(); /** Execute the Set Preferences command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS SetPreferences( IN struct Command *pCmd ); #endif /** _SET_PREFERENCES_COMMAND_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/SetSensorCommand.c000066400000000000000000000224541440615110200213060ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include "SetSensorCommand.h" #include #include #include #include #include "Common.h" #include "NvmDimmCli.h" /** Command syntax definition **/ struct Command SetSensorCommand = { SET_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {FORCE_OPTION_SHORT, FORCE_OPTION, L"", L"",HELP_FORCE_DETAILS_TEXT, FALSE, ValueEmpty} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, { //!< targets {SENSOR_TARGET, L"", HELP_TEXT_SENSORS, TRUE, ValueRequired}, {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional} }, { //!< properties {ALARM_THRESHOLD_PROPERTY, L"", HELP_TEXT_VALUE, FALSE}, {ALARM_ENABLED_PROPERTY, L"", PROPERTY_VALUE_0 L"|" PROPERTY_VALUE_1, FALSE} }, L"Modify the alarm threshold(s) for one or more " PMEM_MODULES_STR L".", //!< help SetSensor, TRUE }; /** Execute the set sensor command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS SetSensor( IN struct Command *pCmd ) { EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; UINT16 *pDimmIds = NULL; UINT32 DimmHandle = 0; UINT32 DimmIndex = 0; UINT32 DimmIdsCount = 0; UINT64 TempValue = 0; CHAR16* pTargetValue = NULL; CHAR16* pCommandStatusMessage = NULL; INT16 NonCriticalThreshold = THRESHOLD_UNDEFINED; UINT16 Index = 0; UINT8 EnabledState = ENABLED_STATE_UNDEFINED; UINT8 SensorId = SENSOR_ID_UNDEFINED; BOOLEAN ValidPropertyAndValue = FALSE; BOOLEAN Force = FALSE; EFI_STATUS ReturnCode = EFI_SUCCESS; COMMAND_STATUS *pCommandStatus = NULL; BOOLEAN Confirmation = FALSE; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; PRINT_CONTEXT *pPrinterCtx = NULL; NVDIMM_ENTRY(); ZeroMem(DimmStr, sizeof(DimmStr)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } // check targets if (ContainTarget(pCmd, DIMM_TARGET)) { pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetDimmIdsFromString"); goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsCount)){ ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } if (!AllDimmsInListInSupportedConfig(pDimms, DimmCount, pDimmIds, DimmIdsCount)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_POPULATION_VIOLATION); goto Finish; } } if (DimmIdsCount == 0) { ReturnCode = GetManageableDimmsNumberAndId(pNvmDimmConfigProtocol, TRUE, &DimmIdsCount, &pDimmIds); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } if (DimmIdsCount == 0) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_INFO_NO_MANAGEABLE_DIMMS); goto Finish; } } // check properties if (!EFI_ERROR(ContainsProperty(pCmd, ALARM_THRESHOLD_PROPERTY))) { if (PropertyToUint64(pCmd, ALARM_THRESHOLD_PROPERTY, &TempValue)) { NonCriticalThreshold = (INT16) TempValue; ValidPropertyAndValue = TRUE; } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_PROPERTY_ALARM_THRESHOLD); goto Finish; } } if (!EFI_ERROR(ContainsProperty(pCmd, ALARM_ENABLED_PROPERTY))) { if (PropertyToUint64(pCmd, ALARM_ENABLED_PROPERTY, &TempValue)) { EnabledState = (UINT8) TempValue; ValidPropertyAndValue = TRUE; } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_PROPERTY_ENABLED_STATE); goto Finish; } } if (!ValidPropertyAndValue) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCOMPLETE_SYNTAX); goto Finish; } if (ContainTarget(pCmd, SENSOR_TARGET)) { pTargetValue = GetTargetValue(pCmd, SENSOR_TARGET); if (StrICmp(pTargetValue, CONTROLLER_TEMPERATURE_TARGET_VALUE) == 0) { SensorId = SENSOR_TYPE_CONTROLLER_TEMPERATURE; } else if (StrICmp(pTargetValue, MEDIA_TEMPERATURE_TARGET_VALUE) == 0) { SensorId = SENSOR_TYPE_MEDIA_TEMPERATURE; } else if (StrICmp(pTargetValue, SPARE_CAPACITY_TARGET_VALUE) == 0) { SensorId = SENSOR_TYPE_PERCENTAGE_REMAINING; } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_TARGET_SENSOR); goto Finish; } } /** Check force option **/ if (containsOption(pCmd, FORCE_OPTION) || containsOption(pCmd, FORCE_OPTION_SHORT)) { Force = TRUE; } // initialize status structure ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } if (SensorId == SENSOR_TYPE_CONTROLLER_TEMPERATURE) { pCommandStatusMessage = CatSPrint(NULL, L"Modify controller temperature settings"); } else if (SensorId == SENSOR_TYPE_MEDIA_TEMPERATURE) { pCommandStatusMessage = CatSPrint(NULL, L"Modify media temperature settings"); } else { pCommandStatusMessage = CatSPrint(NULL, L"Modify percentage remaining settings"); } if (!Force) { for (Index = 0; Index < DimmIdsCount; Index++) { ReturnCode = GetDimmHandleByPid(pDimmIds[Index], pDimms, DimmCount, &DimmHandle, &DimmIndex); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } ReturnCode = GetPreferredDimmIdAsString(DimmHandle, pDimms[DimmIndex].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, L"Modifying settings on " PMEM_MODULE_STR L" " FORMAT_STR L".", DimmStr); ReturnCode = PromptYesNo(&Confirmation); if (!EFI_ERROR(ReturnCode) && Confirmation) { ReturnCode = pNvmDimmConfigProtocol->SetAlarmThresholds(pNvmDimmConfigProtocol, &pDimmIds[Index], 1, SensorId, NonCriticalThreshold, EnabledState, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto FinishCommandStatusSet; } } else { PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, L"Skipped modifying settings for " PMEM_MODULE_STR L" " FORMAT_STR L"\n", DimmStr); continue; } } } else { ReturnCode = pNvmDimmConfigProtocol->SetAlarmThresholds(pNvmDimmConfigProtocol, &pDimmIds[Index], DimmIdsCount, SensorId, NonCriticalThreshold, EnabledState, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto FinishCommandStatusSet; } } FinishCommandStatusSet: ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, pCommandStatusMessage, L" on", pCommandStatus); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pCommandStatusMessage); FREE_POOL_SAFE(pDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the set sensor command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterSetSensorCommand( ) { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&SetSensorCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/SetSensorCommand.h000066400000000000000000000013711440615110200213060ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SET_SENSOR_COMMAND_H_ #define _SET_SENSOR_COMMAND_H_ #include #include "CommandParser.h" /** Register the set sensor command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterSetSensorCommand( ); /** Execute the set sensor command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS SetSensor( IN struct Command *pCmd ); #endif /** _SET_SENSOR_COMMAND_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowAcpiCommand.c000066400000000000000000000222301440615110200210660ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include "ShowAcpiCommand.h" #include #include #include #include #include #include "Common.h" #include "NvmDimmCli.h" #include #include /* * PRINT LIST ATTRIBUTES (2 levels: Target-->Type) * ---NVDIMM Firmware Interface Table--- * ---TableType=0X0 * Length: 56 bytes * TypeEquals: SpaRange * ... */ PRINTER_LIST_ATTRIB ShowAcpiListAttributes = { { { ACPI_MODE_STR, //GROUP LEVEL TYPE L"---$(" SYSTEM_TARGET_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT FORMAT_STR L": " FORMAT_STR, //NULL or KEY VAL FORMAT STR SYSTEM_TARGET_STR //NULL or IGNORE KEY LIST (K1;K2) }, { ACPITYPE_MODE_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT L"---" ACPI_TYPE_STR L"=$(" ACPI_TYPE_STR L")", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR L": " FORMAT_STR, //NULL or KEY VAL FORMAT STR ACPI_TYPE_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; PRINTER_DATA_SET_ATTRIBS ShowAcpiDataSetAttribs = { &ShowAcpiListAttributes, NULL }; /* Command syntax definition */ struct Command showAcpiCommand = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"", HELP_VERBOSE_DETAILS_TEXT,FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #else {L"", L"", L"", L"",L"", FALSE, ValueOptional} #endif }, //!< options {{SYSTEM_TARGET, L"", SYSTEM_ACPI_TARGETS, TRUE, ValueOptional}}, //!< targets {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show the ACPI tables related to the " PMEM_MODULES_STR L" in the system.", //!< help showAcpi, //!< run function TRUE //!< enable print control support }; /* * Register the show acpi command */ EFI_STATUS RegisterShowAcpiCommand() { EFI_STATUS rc = EFI_SUCCESS; NVDIMM_ENTRY(); rc = RegisterCommand(&showAcpiCommand); NVDIMM_EXIT_I64(rc); return rc; } /** PrintInvalidAcpiRevisionString - Prints the invalid ACPI revision string @param[in] Revision ACPI Table Revision @param[in] AcpiTableType ACPI Table (NFIT/PCAT/PMTT) @param[in] DisplayAsMajorMinor flag to indicate if Revision needs to be printed as Major & Minor @param[in] pPrinterCtx pointer to command's printer context **/ VOID PrintInvalidAcpiRevisionString( IN ACPI_REVISION Revision, IN AcpiType AcpiTableType, IN BOOLEAN DisplayAsMajorMinor, IN PRINT_CONTEXT *pPrinterCtx ) { CHAR16 *pInvalidAcpiRevisionMessage = NULL; CHAR16 *pAcpiTableTypeString = NULL; if (pPrinterCtx == NULL) { return; } switch (AcpiTableType) { case AcpiNfit: pAcpiTableTypeString = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_ACPI_STATUS_NFIT), NULL); break; case AcpiPcat: pAcpiTableTypeString = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_ACPI_STATUS_PCAT), NULL); break; case AcpiPMTT: pAcpiTableTypeString = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_ACPI_STATUS_PMTT), NULL); break; default: pAcpiTableTypeString = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_ACPI_STATUS_UNKNOWN_TABLE), NULL); break; } if (DisplayAsMajorMinor) { pInvalidAcpiRevisionMessage = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_ACPI_STATUS_ERR_INVALID_MAJOR_MINOR_REVISION), NULL); pInvalidAcpiRevisionMessage = CatSPrintClean(NULL, pInvalidAcpiRevisionMessage, pAcpiTableTypeString, Revision.Split.Major, Revision.Split.Minor); } else { pInvalidAcpiRevisionMessage = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_ACPI_STATUS_ERR_INVALID_REVISION), NULL); pInvalidAcpiRevisionMessage = CatSPrintClean(NULL, pInvalidAcpiRevisionMessage, pAcpiTableTypeString, Revision.AsUint8); } PRINTER_SET_MSG(pPrinterCtx, EFI_INVALID_PARAMETER, FORMAT_STR, pInvalidAcpiRevisionMessage); FREE_POOL_SAFE(pInvalidAcpiRevisionMessage); FREE_POOL_SAFE(pAcpiTableTypeString); } /* * Execute the show acpi command */ EFI_STATUS showAcpi(struct Command *pCmd) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; ParsedFitHeader *pNFit = NULL; ParsedPcatHeader *pPcat = NULL; TABLE_HEADER *pPMTT = NULL; CHAR16 *pSystemTargetValue = NULL; CHAR16 **ppStringElements = NULL; UINT32 ElementsCount = 0; UINT8 ChosenAcpiSystem = AcpiUnknown; UINT16 Index = 0; PRINT_CONTEXT *pPrinterCtx = NULL; NVDIMM_ENTRY(); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); ReturnCode = EFI_NOT_FOUND; goto Finish; } if (!ContainTarget(pCmd, SYSTEM_TARGET)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pSystemTargetValue = GetTargetValue(pCmd, SYSTEM_TARGET); ppStringElements = StrSplit(pSystemTargetValue, L',', &ElementsCount); if (ppStringElements != NULL) { for (Index = 0; Index < ElementsCount; ++Index) { if (StrICmp(ppStringElements[Index], NFIT_TARGET_VALUE) == 0) { ChosenAcpiSystem |= AcpiNfit; } else if (StrICmp(ppStringElements[Index], PCAT_TARGET_VALUE) == 0) { ChosenAcpiSystem |= AcpiPcat; } else if (StrICmp(ppStringElements[Index], PMTT_TARGET_VALUE) == 0) { ChosenAcpiSystem |= AcpiPMTT; } else { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"The provided system ACPI Table: " FORMAT_STR L" is not valid\n", pSystemTargetValue); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } } else { /* If no target value is mentioned, then print all */ ChosenAcpiSystem = AcpiAll; } if (ChosenAcpiSystem == AcpiAll || ChosenAcpiSystem == AcpiNfit) { ReturnCode = pNvmDimmConfigProtocol->GetAcpiNFit(pNvmDimmConfigProtocol, &pNFit); if (EFI_ERROR(ReturnCode) || pNFit == NULL) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Error: Failed to find the NVDIMM Firmware Interface ACPI tables\n"); ReturnCode = EFI_ABORTED; } else if (IS_NFIT_REVISION_INVALID(pNFit->pFit->Header.Revision)) { PrintInvalidAcpiRevisionString(pNFit->pFit->Header.Revision, AcpiNfit, FALSE, pCmd->pPrintCtx); ReturnCode = EFI_UNSUPPORTED; } else { PrintNFit(pNFit, pPrinterCtx); } } if (ChosenAcpiSystem == AcpiAll || ChosenAcpiSystem == AcpiPcat) { ReturnCode = pNvmDimmConfigProtocol->GetAcpiPcat(pNvmDimmConfigProtocol, &pPcat); if (EFI_ERROR(ReturnCode) || pPcat == NULL) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Error: Failed to find the PCAT tables\n"); } else if (IS_PCAT_REVISION_INVALID(pPcat->pPlatformConfigAttr->Header.Revision)) { PrintInvalidAcpiRevisionString(pPcat->pPlatformConfigAttr->Header.Revision, AcpiPcat, TRUE, pCmd->pPrintCtx); ReturnCode = EFI_UNSUPPORTED; } else { PrintPcat(pPcat, pPrinterCtx); } } if (ChosenAcpiSystem == AcpiAll || ChosenAcpiSystem == AcpiPMTT) { ReturnCode = pNvmDimmConfigProtocol->GetAcpiPMTT(pNvmDimmConfigProtocol, (VOID *)&pPMTT); if (ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"PMTT table not found.\n"); ReturnCode = EFI_SUCCESS; } else if (EFI_ERROR(ReturnCode)|| pPMTT == NULL) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Error: Failed to find the PMTT tables\n"); } else if (IS_PMTT_REVISION_INVALID(pPMTT->Revision)) { PrintInvalidAcpiRevisionString(pPMTT->Revision, AcpiPMTT, FALSE, pCmd->pPrintCtx); FREE_POOL_SAFE(pPMTT); ReturnCode = EFI_UNSUPPORTED; } else { PrintPmtt(pPMTT, pPrinterCtx); FREE_POOL_SAFE(pPMTT); } } //Specify list attributes & display dataset as a list PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowAcpiDataSetAttribs); PRINTER_ENABLE_LIST_TABLE_FORMAT(pPrinterCtx); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); if (ppStringElements != NULL) { FreeStringArray(ppStringElements, ElementsCount); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowAcpiCommand.h000066400000000000000000000011021440615110200210660ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_ACPI_COMMAND_H_ #define _SHOW_ACPI_COMMAND_H_ #include "CommandParser.h" typedef enum { AcpiUnknown = 0, AcpiNfit = BIT0, AcpiPcat = BIT1, AcpiPMTT = BIT2, AcpiAll = AcpiUnknown | AcpiNfit | AcpiPcat | AcpiPMTT } AcpiType; /** Register the show ACPI tables command **/ EFI_STATUS RegisterShowAcpiCommand( ); /** Execute the show ACPI command **/ EFI_STATUS showAcpi( IN struct Command *pCmd ); #endif /** _SHOW_ACPI_COMMAND_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowCelCommand.c000066400000000000000000000272421440615110200207250ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /* TODO: Include headers */ #include #include "ShowCelCommand.h" #include "NvmDimmCli.h" #include "NvmInterface.h" #include "LoadCommand.h" #include "Debug.h" #include "Convert.h" #define DS_ROOT_PATH L"/CelList" #define DS_DIMM_PATH L"/CelList/Dimm" #define DS_DIMM_INDEX_PATH L"/CelList/Dimm[%d]" #define DS_CEL_PATH L"/CelList/Dimm/Cel" #define DS_CEL_INDEX_PATH L"/CelList/Dimm[%d]/Cel[%d]" /** show -cel syntax definition **/ struct Command ShowCelCommandSyntax = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", LARGE_PAYLOAD_OPTION, L"", L"", HELP_LPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", SMALL_PAYLOAD_OPTION, L"", L"", HELP_SPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT,FALSE, ValueRequired } #else {L"", L"", L"", L"", L"",FALSE, ValueOptional} #endif }, { {CEL_TARGET, L"", HELP_TEXT_DIMM_IDS, TRUE, ValueEmpty}, {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show command effect log (CEL) for one or more " PMEM_MODULES_STR L".", //!< help ShowCelCommand, //!< run function TRUE }; // Table heading names #define OPCODE_STR L"Opcode" #define SUBOPCODE_STR L"SubOpcode" #define CE_DESCRIPTION_STR L"CE Description" /* * SHOW CAP ATTRIBUTES (4 columns) * DimmID | Opcode | SubOpcode | CE Description * =========================================== * 0x0001 | X | X | X * ... */ PRINTER_TABLE_ATTRIB ShowCelTableAttributes = { { { DIMM_ID_STR, //COLUMN HEADER DIMM_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_PATH PATH_KEY_DELIM DIMM_ID_STR //COLUMN DATA PATH }, { OPCODE_STR, //COLUMN HEADER TABLE_MIN_HEADER_LENGTH(OPCODE_STR), //COLUMN MAX STR WIDTH DS_CEL_PATH PATH_KEY_DELIM OPCODE_STR //COLUMN DATA PATH }, { SUBOPCODE_STR, //COLUMN HEADER TABLE_MIN_HEADER_LENGTH(SUBOPCODE_STR), //COLUMN MAX STR WIDTH DS_CEL_PATH PATH_KEY_DELIM SUBOPCODE_STR //COLUMN DATA PATH }, { CE_DESCRIPTION_STR, //COLUMN HEADER TABLE_MIN_HEADER_LENGTH(CE_DESCRIPTION_STR), //COLUMN MAX STR WIDTH DS_CEL_PATH PATH_KEY_DELIM CE_DESCRIPTION_STR //COLUMN DATA PATH } } }; PRINTER_DATA_SET_ATTRIBS ShowCmdEffectLogDataSetAttribs = { NULL, &ShowCelTableAttributes }; /** Create the string from Command Effect Bits. param[in] Value is the value to be printed. param[in] SensorType - type of sensor **/ STATIC CHAR16 * GetCommandEffectDescriptionStr( IN COMMAND_EFFECT_LOG_ENTRY CelEntry ) { CHAR16 *pReturnBuffer = NULL; // Return immediately if opcode not supported if (0 == CelEntry.EffectName.AsUint32) { pReturnBuffer = CatSPrintClean(pReturnBuffer, OPCODE_NOT_SUPPORTED); return pReturnBuffer; } if (1 == CelEntry.EffectName.Separated.NoEffects) { if (NULL != pReturnBuffer) { pReturnBuffer = CatSPrintClean(pReturnBuffer, L", "); } pReturnBuffer = CatSPrintClean(pReturnBuffer, NO_EFFECTS); } if (1 == CelEntry.EffectName.Separated.SecurityStateChange) { if (NULL != pReturnBuffer) { pReturnBuffer = CatSPrintClean(pReturnBuffer, L", "); } pReturnBuffer = CatSPrintClean(pReturnBuffer, SECURITY_STATE_CHANGE); } if (1 == CelEntry.EffectName.Separated.DimmConfigChangeAfterReboot) { if (NULL != pReturnBuffer) { pReturnBuffer = CatSPrintClean(pReturnBuffer, L", "); } pReturnBuffer = CatSPrintClean(pReturnBuffer, DIMM_CONFIGURATION_CHANGE_AFTER_REBOOT); } if (1 == CelEntry.EffectName.Separated.ImmediateDimmConfigChange) { if (NULL != pReturnBuffer) { pReturnBuffer = CatSPrintClean(pReturnBuffer, L", "); } pReturnBuffer = CatSPrintClean(pReturnBuffer, IMMEDIATE_DIMM_CONFIGURATION_CHANGE); } if (1 == CelEntry.EffectName.Separated.QuiesceAllIo) { if (NULL != pReturnBuffer) { pReturnBuffer = CatSPrintClean(pReturnBuffer, L", "); } pReturnBuffer = CatSPrintClean(pReturnBuffer, QUIESCE_ALL_IO); } if (1 == CelEntry.EffectName.Separated.ImmediateDimmDataChange) { if (NULL != pReturnBuffer) { pReturnBuffer = CatSPrintClean(pReturnBuffer, L", "); } pReturnBuffer = CatSPrintClean(pReturnBuffer, IMMEDIATE_DIMM_DATA_CHANGE); } if (1 == CelEntry.EffectName.Separated.TestMode) { if (NULL != pReturnBuffer) { pReturnBuffer = CatSPrintClean(pReturnBuffer, L", "); } pReturnBuffer = CatSPrintClean(pReturnBuffer, TEST_MODE); } if (1 == CelEntry.EffectName.Separated.DebugMode) { if (NULL != pReturnBuffer) { pReturnBuffer = CatSPrintClean(pReturnBuffer, L", "); } pReturnBuffer = CatSPrintClean(pReturnBuffer, DEBUG_MODE); } if (1 == CelEntry.EffectName.Separated.ImmediateDimmPolicyChange) { if (NULL != pReturnBuffer) { pReturnBuffer = CatSPrintClean(pReturnBuffer, L", "); } pReturnBuffer = CatSPrintClean(pReturnBuffer, IMMEDIATE_DIMM_POLICY_CHANGE); } return pReturnBuffer; } /** Register the show -cel command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowCelCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowCelCommandSyntax); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get command effect log command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS ShowCelCommand( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PRINT_CONTEXT *pPrinterCtx = NULL; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; CHAR16 *pDimmsValue = NULL; UINT16 *pDimmIds = NULL; UINT32 DimmIdsNum = 0; UINT32 DimmIndex = 0; COMMAND_EFFECT_LOG_ENTRY *pCelEntry = NULL; UINT32 EntryCount = 0; UINT32 DimmHandle = 0; UINT32 DimmIdIndex = 0; UINT32 CelEntryIndex = 0; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; CHAR16 *pPath = NULL; CHAR16 *pCommandEffectDescription = NULL; NVDIMM_ENTRY(); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } // check targets if (ContainTarget(pCmd, DIMM_TARGET)) { pDimmsValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pDimmsValue, pDimms, DimmCount, &pDimmIds, &DimmIdsNum); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Target value is not a valid Dimm ID"); goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsNum)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } if (!AllDimmsInListInSupportedConfig(pDimms, DimmCount, pDimmIds, DimmIdsNum)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_POPULATION_VIOLATION); goto Finish; } } // If no dimm IDs are specified get IDs from all dimms if (DimmIdsNum == 0) { ReturnCode = GetManageableDimmsNumberAndId(pNvmDimmConfigProtocol, TRUE, &DimmIdsNum, &pDimmIds); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } if (DimmIdsNum == 0) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_INFO_NO_MANAGEABLE_DIMMS); goto Finish; } } // Traverse each DIMM for (DimmIndex = 0; DimmIndex < DimmCount; DimmIndex++) { if (!ContainUint(pDimmIds, DimmIdsNum, pDimms[DimmIndex].DimmID)) { continue; } if ((MANAGEMENT_VALID_CONFIG != pDimms[DimmIndex].ManageabilityState) || (TRUE == pDimms[DimmIndex].IsInPopulationViolation)){ continue; } // Get CEL table for each DIMM ReturnCode = pNvmDimmConfigProtocol->GetCommandEffectLog(pNvmDimmConfigProtocol, pDimms[DimmIndex].DimmID, &pCelEntry, &EntryCount); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } // Retrieve DimmHandle and DimmIdIndex for given DimmId ReturnCode = GetDimmHandleByPid(pDimms[DimmIndex].DimmID, pDimms, DimmCount, &DimmHandle, &DimmIdIndex); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } // Retrieve DimmId as string based on preferences ReturnCode = GetPreferredDimmIdAsString(pDimms[DimmIdIndex].DimmHandle, pDimms[DimmIdIndex].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_INDEX_PATH, DimmIndex); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, DimmStr); for (CelEntryIndex = 0; CelEntryIndex < EntryCount; CelEntryIndex++) { PRINTER_BUILD_KEY_PATH(pPath, DS_CEL_INDEX_PATH, DimmIndex, CelEntryIndex); PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, OPCODE_STR, (UINT8)pCelEntry[CelEntryIndex].Opcode.Separated.Opcode, HEX); PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, SUBOPCODE_STR, (UINT8)pCelEntry[CelEntryIndex].Opcode.Separated.SubOpcode, HEX); pCommandEffectDescription = GetCommandEffectDescriptionStr(pCelEntry[CelEntryIndex]); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, CE_DESCRIPTION_STR, pCommandEffectDescription); FREE_POOL_SAFE(pCommandEffectDescription); } FREE_POOL_SAFE(pCelEntry); } //Switch text output type to display as a table PRINTER_ENABLE_TEXT_TABLE_FORMAT(pPrinterCtx); //Specify table attributes PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowCmdEffectLogDataSetAttribs); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pPath); FREE_POOL_SAFE(pCommandEffectDescription); FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pCelEntry); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowCelCommand.h000066400000000000000000000022051440615110200207220ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_CEL_COMMAND_H_ #define _SHOW_CEL_COMMAND_H_ #include #include "NvmInterface.h" #include "Common.h" // Command Effect Description strings #define NO_EFFECTS L"NE" #define SECURITY_STATE_CHANGE L"SSC" #define DIMM_CONFIGURATION_CHANGE_AFTER_REBOOT L"DCC" #define IMMEDIATE_DIMM_CONFIGURATION_CHANGE L"IDCC" #define QUIESCE_ALL_IO L"QIO" #define IMMEDIATE_DIMM_DATA_CHANGE L"IDDC" #define TEST_MODE L"TM" #define DEBUG_MODE L"DM" #define IMMEDIATE_DIMM_POLICY_CHANGE L"IDPC" #define OPCODE_NOT_SUPPORTED L"N/A" /** Register syntax of show -error **/ EFI_STATUS RegisterShowCelCommand( ); /** Get command effect log command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS ShowCelCommand( IN struct Command *pCmd ); #endifipmctl-03.00.00.0485/DcpmPkg/cli/ShowCmdAccessPolicyCommand.c000066400000000000000000000272301440615110200232240ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include "Debug.h" #include "Types.h" #include "Utility.h" #include "NvmDimmCli.h" #include "NvmInterface.h" #include "CommandParser.h" #include "ShowCmdAccessPolicyCommand.h" #include "Common.h" #include "Convert.h" #define DS_ROOT_PATH L"/OpCodePolicyList" #define DS_DIMM_PATH L"/OpCodePolicyList/Dimm" #define DS_DIMM_INDEX_PATH L"/OpCodePolicyList/Dimm[%d]" #define DS_OPCODE_PATH L"/OpCodePolicyList/Dimm/OpCode" #define DS_OPCODE_INDEX_PATH L"/OpCodePolicyList/Dimm[%d]/OpCode[%d]" /** Command syntax definition **/ struct Command ShowCmdAccessPolicyCommand = { SHOW_VERB, //!< verb /** options **/ { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"", HELP_VERBOSE_DETAILS_TEXT,FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #else {L"", L"", L"", L"", L"",FALSE, ValueOptional} #endif }, /** targets **/ { { DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional }, { CAP_TARGET, L"", L"", TRUE, ValueEmpty }, }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show command access policy (CAP) restrictions for one ore more " PMEM_MODULE_STR L"(s).", //!< help ShowCmdAccessPolicy, //!< run function TRUE, //!< enable print control support }; // Table heading names #define OPCODE_STR L"Opcode" #define SUBOPCODE_STR L"SubOpcode" #define RESTRICTION_STR L"Restriction" /* * SHOW CAP ATTRIBUTES (4 columns) * DimmID | Opcode | SubOpcode | Restricted * =========================================== * 0x0001 | X | X | X * ... */ PRINTER_TABLE_ATTRIB ShowCapTableAttributes = { { { DIMM_ID_STR, //COLUMN HEADER DIMM_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_PATH PATH_KEY_DELIM DIMM_ID_STR //COLUMN DATA PATH }, { OPCODE_STR, //COLUMN HEADER TABLE_MIN_HEADER_LENGTH(OPCODE_STR), //COLUMN MAX STR WIDTH DS_OPCODE_PATH PATH_KEY_DELIM OPCODE_STR //COLUMN DATA PATH }, { SUBOPCODE_STR, //COLUMN HEADER TABLE_MIN_HEADER_LENGTH(SUBOPCODE_STR), //COLUMN MAX STR WIDTH DS_OPCODE_PATH PATH_KEY_DELIM SUBOPCODE_STR //COLUMN DATA PATH }, { RESTRICTION_STR, //COLUMN HEADER CAP_MB_RESTRICTION_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_OPCODE_PATH PATH_KEY_DELIM RESTRICTION_STR //COLUMN DATA PATH } } }; PRINTER_DATA_SET_ATTRIBS ShowCmdAccessPolicyDataSetAttribs = { NULL, &ShowCapTableAttributes }; /** Register the show cmd access policy command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowCmdAccessPolicyCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowCmdAccessPolicyCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Execute the show cmd access policy command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_NOT_FOUND failed to open Config protocol, or run-time preferences could not be retrieved @retval Other errors returned by the driver **/ EFI_STATUS ShowCmdAccessPolicy( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; UINT32 DimmIndex = 0; UINT32 OpCodeIndex = 0; CHAR16 *pTargetValue = NULL; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; UINT16 *pDimmIds = NULL; UINT32 DimmIdsCount = 0; COMMAND_ACCESS_POLICY_ENTRY *pCapEntries = NULL; COMMAND_ACCESS_POLICY_ENTRY *pCapEntry = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; UINT32 DimmHandle = 0; UINT32 DimmIdIndex = 0; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; CHAR16 *RestrictionStr = NULL; UINT32 CapCount = 0; NVDIMM_ENTRY(); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; /** Make sure we can access the config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } // check targets if (ContainTarget(pCmd, DIMM_TARGET)) { pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetDimmIdsFromString"); goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsCount)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } } /** If no dimm IDs are specified get IDs from all dimms **/ if (DimmIdsCount == 0) { ReturnCode = GetAllManageableDimmsNumberAndId(pNvmDimmConfigProtocol, FALSE, &DimmIdsCount, &pDimmIds); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } if (DimmIdsCount == 0) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_INFO_NO_MANAGEABLE_DIMMS); goto Finish; } } /** Retrieve the count of access policy entries. **/ ReturnCode = pNvmDimmConfigProtocol->GetCommandAccessPolicy(pNvmDimmConfigProtocol, pDimmIds[0], &CapCount, NULL); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } pCapEntries = AllocateZeroPool(sizeof(*pCapEntries) * CapCount * DimmIdsCount); if (pCapEntries == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } for (DimmIndex = 0; DimmIndex < DimmIdsCount; DimmIndex++) { /** Retrieve all CAP for each DIMM **/ ReturnCode = pNvmDimmConfigProtocol->GetCommandAccessPolicy(pNvmDimmConfigProtocol, pDimmIds[DimmIndex], &CapCount, &pCapEntries[DimmIndex*CapCount]); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to get the access policy for pDimmIds[%d] - ReturnCode=0x%x", DimmIndex, ReturnCode); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } /* Retrieve DimmHandle and DimmIdIndex for given DimmId */ ReturnCode = GetDimmHandleByPid(pDimmIds[DimmIndex], pDimms, DimmCount, &DimmHandle, &DimmIdIndex); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } /* Retrieve DimmId as string based on preferences */ ReturnCode = GetPreferredDimmIdAsString(pDimms[DimmIdIndex].DimmHandle, pDimms[DimmIdIndex].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_INDEX_PATH, DimmIndex); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, DimmStr); for (OpCodeIndex = 0; OpCodeIndex < CapCount; OpCodeIndex++) { PRINTER_BUILD_KEY_PATH(pPath, DS_OPCODE_INDEX_PATH, DimmIndex, OpCodeIndex); pCapEntry = &(pCapEntries[DimmIndex * CapCount + OpCodeIndex]); PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, OPCODE_STR, pCapEntry->Opcode, HEX); PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, SUBOPCODE_STR, pCapEntry->SubOpcode, HEX); switch (pCapEntry->Restriction) { case COMMAND_ACCESS_POLICY_RESTRICTION_NONE: RestrictionStr = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_DCPMM_RESTRICTION_NONE), NULL); break; case COMMAND_ACCESS_POLICY_RESTRICTION_BIOSONLY: RestrictionStr = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_DCPMM_RESTRICTION_BIOS_ONLY), NULL); break; case COMMAND_ACCESS_POLICY_RESTRICTION_SMBUSONLY: RestrictionStr = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_DCPMM_RESTRICTION_SMBUS_ONLY), NULL); break; case COMMAND_ACCESS_POLICY_RESTRICTION_BIOSSMBUSONLY: RestrictionStr = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_DCPMM_RESTRICTION_BIOS_SMBUS_ONLY), NULL); break; case COMMAND_ACCESS_POLICY_RESTRICTION_MGMTONLY: RestrictionStr = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_DCPMM_RESTRICTION_MGMT_ONLY), NULL); break; case COMMAND_ACCESS_POLICY_RESTRICTION_MGMTBIOSONLY: RestrictionStr = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_DCPMM_RESTRICTION_MGMT_BIOS_ONLY), NULL); break; case COMMAND_ACCESS_POLICY_RESTRICTION_MGMTSMBUSONLY: RestrictionStr = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_DCPMM_RESTRICTION_MGMT_SMBUS_ONLY), NULL); break; case COMMAND_ACCESS_POLICY_RESTRICTION_MGMTBIOSSMBUSONLY: RestrictionStr = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_DCPMM_RESTRICTION_MGMT_BIOS_SMBUS_ONLY), NULL); break; case COMMAND_ACCESS_POLICY_RESTRICTION_UNSUPPORTED: RestrictionStr = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_DCPMM_RESTRICTION_UNSUPPORTED), NULL);; break; default: RestrictionStr = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_DCPMM_RESTRICTION_INVALID), NULL); break; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, RESTRICTION_STR, RestrictionStr); FREE_POOL_SAFE(RestrictionStr); } } //Switch text output type to display as a table PRINTER_ENABLE_TEXT_TABLE_FORMAT(pPrinterCtx); //Specify table attributes PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowCmdAccessPolicyDataSetAttribs); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(RestrictionStr); FREE_POOL_SAFE(pPath); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pCapEntries); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowCmdAccessPolicyCommand.h000066400000000000000000000016651440615110200232350ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_CAP_COMMAND_H_ #define _SHOW_CAP_COMMAND_H_ #include "CommandParser.h" /** Register the show cmd access policy command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowCmdAccessPolicyCommand( ); /** Execute the show cmd access policy command @param[in] pCmd command from CLI @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_NOT_FOUND failed to open Config protocol, or run-time preferences could not be retrieved @retval Other errors returned by the driver **/ EFI_STATUS ShowCmdAccessPolicy( IN struct Command *pCmd ); #endif /* _SHOW_CAP_COMMAND_H_*/ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowDimmsCommand.c000066400000000000000000002221741440615110200212740ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include "ShowDimmsCommand.h" #include #include #include #include #include #include "Common.h" #include "NvmDimmCli.h" #include #include #include #include #include #include #define DS_ROOT_PATH L"/DimmList" #define DS_DIMM_PATH L"/DimmList/Dimm" #define DS_DIMM_INDEX_PATH L"/DimmList/Dimm[%d]" /* * PRINT LIST ATTRIBUTES * ---DimmId=0x0001--- * Capacity=125.7 GiB * LockState=Locked * HealthState=Healthy * ... */ PRINTER_LIST_ATTRIB ShowDimmListAttributes = { { { DIMM_NODE_STR, //GROUP LEVEL TYPE L"---" DIMM_ID_STR L"=$(" DIMM_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR DIMM_ID_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; /* * PRINTER TABLE ATTRIBUTES (5 columns) * DimmID | Capacity | LockState | HealthState | FWVersion * ======================================================== * 0x0001 | X | X | X | X * ... */ PRINTER_TABLE_ATTRIB ShowDimmTableAttributes = { { { DIMM_ID_STR, //COLUMN HEADER DIMM_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_PATH PATH_KEY_DELIM DIMM_ID_STR //COLUMN DATA PATH }, { CAPACITY_STR, //COLUMN HEADER CAPACITY_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_PATH PATH_KEY_DELIM CAPACITY_STR //COLUMN DATA PATH }, { SECURITY_STR, //COLUMN HEADER SECURITY_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_PATH PATH_KEY_DELIM SECURITY_STR //COLUMN DATA PATH }, { HEALTH_STR, //COLUMN HEADER HEALTH_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_PATH PATH_KEY_DELIM HEALTH_STR //COLUMN DATA PATH }, { FW_VER_STR, //COLUMN HEADER FW_VERSION_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_PATH PATH_KEY_DELIM FW_VER_STR //COLUMN DATA PATH } } }; PRINTER_DATA_SET_ATTRIBS ShowDimmDataSetAttribs = { &ShowDimmListAttributes, &ShowDimmTableAttributes }; extern BOOLEAN gDisplayNulls; extern UINT32 gNullValuesEncounteredForDisplay; extern CHAR16* gNullValueToDisplay; /* Command syntax definition */ struct Command ShowDimmsCommand = { SHOW_VERB, //!< verb /** options **/ { {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {ALL_OPTION_SHORT, ALL_OPTION, L"", L"",HELP_ALL_DETAILS_TEXT, FALSE, ValueEmpty}, {DISPLAY_OPTION_SHORT, DISPLAY_OPTION, L"", HELP_TEXT_ATTRIBUTES,HELP_DISPLAY_DETAILS_TEXT, FALSE, ValueRequired}, {UNITS_OPTION_SHORT, UNITS_OPTION, L"", UNITS_OPTION_HELP,HELP_UNIT_DETAILS_TEXT, FALSE, ValueRequired} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, /** targets **/ { {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, TRUE, ValueOptional}, {SOCKET_TARGET, L"", HELP_TEXT_SOCKET_IDS, FALSE, ValueOptional} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show information about one or more " PMEM_MODULES_STR L".", //!< help ShowDimms, //!< run function TRUE, //!< enable print control support }; CHAR16 *mppAllowedShowDimmsDisplayValues[] = { DIMM_ID_STR, SOCKET_ID_STR, FW_VER_STR, FW_API_VER_STR, FW_ACTIVE_API_VER_STR, INTERFACE_FORMAT_CODE_STR, CAPACITY_STR, MANAGEABILITY_STR, POPULATION_VIOLATION_STR, SECURITY_STR, SVN_DOWNGRADE_OPT_IN_STR, SEP_OPT_IN_STR, S3_RESUME_OPT_IN_STR, FW_ACTIVATE_OPT_IN_STR, HEALTH_STR, HEALTH_STATE_REASON_STR, FORM_FACTOR_STR, VENDOR_ID_STR, MANUFACTURER_ID_STR, DEVICE_ID_STR, REVISION_ID_STR, SUBSYSTEM_VENDOR_ID_STR, SUBSYSTEM_DEVICE_ID_STR, SUBSYSTEM_REVISION_ID_STR, CONTROLLER_REVISION_ID_STR, MANUFACTURING_INFO_VALID, MANUFACTURING_LOCATION, MANUFACTURING_DATE, PART_NUMBER_STR, SERIAL_NUMBER_STR, DEVICE_LOCATOR_STR, MEMORY_CONTROLLER_STR, DATA_WIDTH_STR, TOTAL_WIDTH_STR, SPEED_STR, MEMORY_MODE_CAPACITY_STR, APPDIRECT_MODE_CAPACITY_STR, UNCONFIGURED_CAPACITY_STR, PACKAGE_SPARING_ENABLED_STR, PACKAGE_SPARING_CAPABLE_STR, PACKAGE_SPARES_AVAILABLE_STR, IS_NEW_STR, BANK_LABEL_STR, MEMORY_TYPE_STR, AVG_PWR_REPORTING_TIME_CONSTANT, MANUFACTURER_STR, CHANNEL_ID_STR, SLOT_ID_STR, CHANNEL_POS_STR, PEAK_POWER_BUDGET_STR, AVG_POWER_LIMIT_STR, AVG_POWER_TIME_CONSTANT_STR, MEMORY_BANDWIDTH_BOOST_FEATURE_STR, MEMORY_BANDWIDTH_BOOST_MAX_POWER_LIMIT_STR, MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT_STR, MAX_AVG_POWER_LIMIT_STR, MAX_MEMORY_BANDWIDTH_BOOST_MAX_POWER_LIMIT, MAX_MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT, MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT_STEP, MAX_AVERAGE_POWER_REPORTING_TIME_CONSTANT, AVERAGE_POWER_REPORTING_TIME_CONSTANT_STEP, LATCHED_LAST_SHUTDOWN_STATUS_STR, UNLATCHED_LAST_SHUTDOWN_STATUS_STR, MAX_MEDIA_TEMPERATURE_STR, MAX_CONTROLLER_TEMPERATURE_STR, THERMAL_THROTTLE_LOSS_STR, DIMM_HANDLE_STR, DIMM_UID_STR, MODES_SUPPORTED_STR, SECURITY_CAPABILITIES_STR, MASTER_PASS_ENABLED_STR, DIMM_CONFIG_STATUS_STR, SKU_VIOLATION_STR, ARS_STATUS_STR, OVERWRITE_STATUS_STR, LAST_SHUTDOWN_TIME_STR, INACCESSIBLE_CAPACITY_STR, RESERVED_CAPACITY_STR, VIRAL_POLICY_STR, VIRAL_STATE_STR, AIT_DRAM_ENABLED_STR, BOOT_STATUS_STR, PHYSICAL_ID_STR, ERROR_INJECT_ENABLED_STR, MEDIA_TEMP_INJ_ENABLED_STR, SW_TRIGGERS_ENABLED_STR, SW_TRIGGER_ENABLED_DETAILS_STR, POISON_ERR_INJ_CTR_STR, POISON_ERR_CLR_CTR_STR, MEDIA_TEMP_INJ_CTR_STR, SW_TRIGGER_CTR_STR, BOOT_STATUS_REGISTER_STR, DCPMM_AVERAGE_POWER_STR, AVERAGE_12V_POWER_STR, AVERAGE_1_2V_POWER_STR, EXTENDED_ADR_ENABLED_STR, PPC_EXTENDED_ADR_ENABLED_STR, LATCH_SYSTEM_SHUTDOWN_STATE_STR, PREV_PWR_CYCLE_LATCH_SYSTEM_SHUTDOWN_STATE_STR, MIXED_SKU_STR, FIPS_MODE_STATUS_STR }; CHAR16 *mppAllowedShowDimmsConfigStatuses[] = { CONFIG_STATUS_VALUE_VALID, CONFIG_STATUS_VALUE_NOT_CONFIG, CONFIG_STATUS_VALUE_BAD_CONFIG, CONFIG_STATUS_VALUE_BROKEN_INTERLEAVE, CONFIG_STATUS_VALUE_REVERTED, CONFIG_STATUS_VALUE_UNSUPPORTED, CONFIG_STATUS_VALUE_PARTIALLY_SUPPORTED, }; CHAR16 *pOnlyManageableAllowedDisplayValues[] = { MANUFACTURER_ID_STR, CONTROLLER_REVISION_ID_STR, MEMORY_MODE_CAPACITY_STR, APPDIRECT_MODE_CAPACITY_STR, UNCONFIGURED_CAPACITY_STR, INACCESSIBLE_CAPACITY_STR, RESERVED_CAPACITY_STR, PACKAGE_SPARING_CAPABLE_STR, PACKAGE_SPARING_ENABLED_STR, PACKAGE_SPARES_AVAILABLE_STR, IS_NEW_STR, AVG_PWR_REPORTING_TIME_CONSTANT, VIRAL_POLICY_STR, VIRAL_STATE_STR, PEAK_POWER_BUDGET_STR, AVG_POWER_LIMIT_STR, AVG_POWER_TIME_CONSTANT_STR, MEMORY_BANDWIDTH_BOOST_FEATURE_STR, MEMORY_BANDWIDTH_BOOST_MAX_POWER_LIMIT_STR, MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT_STR, MAX_AVG_POWER_LIMIT_STR, MAX_MEMORY_BANDWIDTH_BOOST_MAX_POWER_LIMIT, MAX_MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT, MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT_STEP, MAX_AVERAGE_POWER_REPORTING_TIME_CONSTANT, AVERAGE_POWER_REPORTING_TIME_CONSTANT_STEP, LATCHED_LAST_SHUTDOWN_STATUS_STR, UNLATCHED_LAST_SHUTDOWN_STATUS_STR, MAX_MEDIA_TEMPERATURE_STR, MAX_CONTROLLER_TEMPERATURE_STR, THERMAL_THROTTLE_LOSS_STR, LAST_SHUTDOWN_TIME_STR, MODES_SUPPORTED_STR, SECURITY_CAPABILITIES_STR, MASTER_PASS_ENABLED_STR, DIMM_CONFIG_STATUS_STR, SKU_VIOLATION_STR, ARS_STATUS_STR, OVERWRITE_STATUS_STR, AIT_DRAM_ENABLED_STR, BOOT_STATUS_STR, ERROR_INJECT_ENABLED_STR, MEDIA_TEMP_INJ_ENABLED_STR, SW_TRIGGERS_ENABLED_STR, SW_TRIGGER_ENABLED_DETAILS_STR, POISON_ERR_INJ_CTR_STR, POISON_ERR_CLR_CTR_STR, MEDIA_TEMP_INJ_CTR_STR, SW_TRIGGER_CTR_STR, BOOT_STATUS_REGISTER_STR, DCPMM_AVERAGE_POWER_STR, AVERAGE_12V_POWER_STR, AVERAGE_1_2V_POWER_STR, EXTENDED_ADR_ENABLED_STR, PPC_EXTENDED_ADR_ENABLED_STR, LATCH_SYSTEM_SHUTDOWN_STATE_STR, PREV_PWR_CYCLE_LATCH_SYSTEM_SHUTDOWN_STATE_STR, MIXED_SKU_STR, FIPS_MODE_STATUS_STR }; /* local functions */ STATIC CHAR16 *ManageabilityToString(UINT8 ManageabilityState); STATIC CHAR16 *PopulationViolationToString(UINT8 ManageabilityState); STATIC CHAR16 *FormFactorToString(UINT8 FormFactor); /* * Register the show dimms command */ EFI_STATUS RegisterShowDimmsCommand( ) { EFI_STATUS Rc = EFI_SUCCESS; NVDIMM_ENTRY(); Rc = RegisterCommand(&ShowDimmsCommand); NVDIMM_EXIT_I64(Rc); return Rc; } /** Get manageability state for Dimm @param[in] pDimm the DIMM_INFO struct @retval BOOLEAN whether or not dimm is manageable **/ BOOLEAN IsDimmManageableByDimmInfo( IN DIMM_INFO *pDimm ) { if (pDimm == NULL) { return FALSE; } return IsDimmManageableByValues(pDimm->SubsystemVendorId, pDimm->InterfaceFormatCodeNum, pDimm->InterfaceFormatCode, pDimm->SubsystemDeviceId, pDimm->FwVer.FwApiMajor, pDimm->FwVer.FwApiMinor); } EFI_STATUS IsDimmsMixedSkuCfg(PRINT_CONTEXT *pPrinterCtx, EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, BOOLEAN *pIsMixedSku, BOOLEAN *pIsSkuViolation) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 DimmCount = 0; DIMM_INFO *pDimms = NULL; UINT32 i; ReturnCode = pNvmDimmConfigProtocol->GetDimmCount(pNvmDimmConfigProtocol, &DimmCount); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); return ReturnCode; } pDimms = AllocateZeroPool(sizeof(*pDimms) * DimmCount); if (pDimms == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); return ReturnCode; } /** retrieve the DIMM list **/ ReturnCode = pNvmDimmConfigProtocol->GetDimms(pNvmDimmConfigProtocol, DimmCount, DIMM_INFO_CATEGORY_PACKAGE_SPARING, pDimms); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_ABORTED; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_WARN("Failed to retrieve the DIMM inventory found in NFIT"); goto Finish; } *pIsMixedSku = FALSE; *pIsSkuViolation = FALSE; for (i = 0; i < DimmCount; ++i) { if (FALSE == IsDimmManageableByDimmInfo(&pDimms[i])) { continue; } if (pDimms[i].SKUViolation) { *pIsSkuViolation = TRUE; } if (NVM_SUCCESS != SkuComparison(pDimms[0].SkuInformation, pDimms[i].SkuInformation)) { *pIsMixedSku = TRUE; } } Finish: FreePool(pDimms); return ReturnCode; } /** Execute the show dimms command **/ EFI_STATUS ShowDimms( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TempReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; UINT32 DimmCount = 0; DIMM_INFO *pDimms = NULL; DIMM_INFO *pAllDimms = NULL; UINT16 *pSocketIds = NULL; UINT32 SocketsNum = 0; UINT16 *pDimmIds = NULL; UINT32 DimmIdsNum = 0; CHAR16 *pSocketsValue = NULL; CHAR16 *pSecurityStr = NULL; CHAR16 *pSVNDowngradeStr = NULL; CHAR16 *pSecureErasePolicyStr = NULL; CHAR16 *pS3ResumeStr = NULL; CHAR16 *pFwActivateStr = NULL; CHAR16 *pHealthStr = NULL; CHAR16 *pHealthStateReasonStr = NULL; CHAR16 *pManageabilityStr = NULL; CHAR16 *pPopulationViolationStr = NULL; CHAR16 *pFormFactorStr = NULL; CHAR16 *pDimmsValue = NULL; CHAR16 TmpFwVerString[MAX(FW_VERSION_LEN, FW_API_VERSION_LEN)]; UINT32 DimmIndex = 0; UINT32 Index1 = 0; UINT32 Index2 = 0; UINT32 Index3 = 0; UINT16 UnitsOption = DISPLAY_SIZE_UNIT_UNKNOWN; UINT16 UnitsToDisplay = FixedPcdGet16(PcdDcpmmCliDefaultCapacityUnit); BOOLEAN Found = FALSE; BOOLEAN ShowAll = FALSE; BOOLEAN ShowTableView = FALSE; BOOLEAN ContainSocketTarget = FALSE; COMMAND_STATUS *pCommandStatus = NULL; CHAR16 *pAttributeStr = NULL; CHAR16 *pCapacityStr = NULL; CHAR16 *pDimmErrStr = NULL; CHAR16 *pOriginalNullVal = NULL; LAST_SHUTDOWN_STATUS_DETAILS_COMBINED LatchedLastShutdownStatusDetails; LAST_SHUTDOWN_STATUS_DETAILS_COMBINED UnlatchedLastShutdownStatusDetails; DISPLAY_PREFERENCES DisplayPreferences; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; BOOLEAN ByteAddressable = FALSE; UINT16 BootStatusBitMask = 0; UINT64 BootStatusRegister = 0; CHAR16 *pSteppingStr = NULL; CMD_DISPLAY_OPTIONS *pDispOptions = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; BOOLEAN volatile DimmIsOkToDisplay[MAX_DIMMS]; BOOLEAN IsMixedSku; BOOLEAN IsSkuViolation; DIMM_INFO_CATEGORIES DimmCategories = DIMM_INFO_CATEGORY_NONE; BOOLEAN FIS_2_0 = FALSE; CHAR16 *pStr = NULL; FIPS_MODE FIPSMode; NVDIMM_ENTRY(); gNullValuesEncounteredForDisplay = 0; ZeroMem(TmpFwVerString, sizeof(TmpFwVerString)); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); ZeroMem(DimmStr, sizeof(DimmStr)); ZeroMem(&LatchedLastShutdownStatusDetails, sizeof(LatchedLastShutdownStatusDetails)); ZeroMem(&UnlatchedLastShutdownStatusDetails, sizeof(UnlatchedLastShutdownStatusDetails)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } for (Index1 = 0; Index1 < MAX_DIMMS; Index1++) { DimmIsOkToDisplay[Index1] = FALSE; } pPrinterCtx = pCmd->pPrintCtx; pDispOptions = AllocateZeroPool(sizeof(CMD_DISPLAY_OPTIONS)); if (NULL == pDispOptions) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = CheckAllAndDisplayOptions(pCmd, mppAllowedShowDimmsDisplayValues, ALLOWED_DISP_VALUES_COUNT(mppAllowedShowDimmsDisplayValues), pDispOptions); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CheckAllAndDisplayOptions has returned error. Code " FORMAT_EFI_STATUS "\n", ReturnCode); goto Finish; } ContainSocketTarget = ContainTarget(pCmd, SOCKET_TARGET); /** if sockets were specified **/ if (ContainSocketTarget) { pSocketsValue = GetTargetValue(pCmd, SOCKET_TARGET); ReturnCode = GetUintsFromString(pSocketsValue, &pSocketIds, &SocketsNum); if (EFI_ERROR(ReturnCode)) { /** Error Code returned by function above **/ NVDIMM_DBG("GetUintsFromString returned error"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_TARGET_SOCKET); goto Finish; } } ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } UnitsToDisplay = DisplayPreferences.SizeUnit; ReturnCode = GetUnitsOption(pCmd, &UnitsOption); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Any valid units option will override the preferences **/ if (UnitsOption != DISPLAY_SIZE_UNIT_UNKNOWN) { UnitsToDisplay = UnitsOption; } /** make sure we can access the config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // initialize status structure ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } ShowTableView = !pDispOptions->AllOptionSet && !pDispOptions->DisplayOptionSet; if (ShowTableView) { DimmCategories = DIMM_INFO_CATEGORY_SECURITY | DIMM_INFO_CATEGORY_SMART_AND_HEALTH; } else { DimmCategories = DIMM_INFO_CATEGORY_ALL; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DimmCategories, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode) || (pDimms == NULL)) { NVDIMM_WARN("Failed to populate the list of DIMM_INFO structures"); goto Finish; } ReturnCode = IsDimmsMixedSkuCfg(pPrinterCtx, pNvmDimmConfigProtocol, &IsMixedSku, &IsSkuViolation); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (IsMixedSku) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, WARNING_DIMMS_SKU_MIXED); NVDIMM_WARN("Mixed SKU detected. Driver functionalities limited."); } /** if a specific DIMM pid was passed in, set it **/ if (NULL != pCmd->targets[0].pTargetValueStr && StrLen(pCmd->targets[0].pTargetValueStr) > 0) { pAllDimms = AllocateZeroPool(sizeof(*pAllDimms) * (DimmCount)); if (NULL == pAllDimms) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } CopyMem_S(pAllDimms, sizeof(*pAllDimms) * (DimmCount), pDimms, sizeof(*pDimms) * DimmCount); pDimmsValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pDimmsValue, pAllDimms, DimmCount, &pDimmIds, &DimmIdsNum); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Target value is not a valid Dimm ID"); goto Finish; } /*Mark each dimm as ok to display based on the dimms passed by the user*/ for (Index1 = 0; Index1 < DimmCount; Index1++) { for (Index2 = 0; Index2 < DimmIdsNum; Index2++) { if (pAllDimms[Index1].DimmID == pDimmIds[Index2]) { DimmIsOkToDisplay[Index1] = TRUE; } } } } else { /*Since no dimms were specified, mark them all as ok to display*/ for (DimmIndex = 0; DimmIndex < MAX_DIMMS; DimmIndex++) { DimmIsOkToDisplay[DimmIndex] = TRUE; } } if (SocketsNum > 0) { Found = FALSE; /*Only display sockets which match the dimms that the user has indicated*/ for (DimmIndex = 0; DimmIndex < DimmCount; DimmIndex++) { if (DimmIsOkToDisplay[DimmIndex] == TRUE && ContainUint(pSocketIds, SocketsNum, pDimms[DimmIndex].SocketId)) { Found = TRUE; break; } } if (!Found) { ReturnCode = EFI_NOT_FOUND; if (DimmIdsNum > 0) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_SPECIFIED_DIMMS_ON_SPECIFIED_SOCKET); } else { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_DIMMS_ON_SOCKET); } NVDIMM_DBG("No DIMMs on provided Socket"); goto Finish; } } /** display a summary table of all dimms **/ if (ShowTableView) { for (DimmIndex = 0; DimmIndex < DimmCount; DimmIndex++) { if (SocketsNum > 0 && !ContainUint(pSocketIds, SocketsNum, pDimms[DimmIndex].SocketId)) { continue; } if (DimmIdsNum > 0 && !ContainUint(pDimmIds, DimmIdsNum, pDimms[DimmIndex].DimmID)) { continue; } PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_INDEX_PATH, DimmIndex); ReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].Capacity, UnitsToDisplay, TRUE, &pCapacityStr); pHealthStr = HealthToString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].HealthState); if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_SECURITY_INFO || MANAGEMENT_VALID_CONFIG != pDimms[DimmIndex].ManageabilityState) { pSecurityStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { pSecurityStr = SecurityStateBitmaskToString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].SecurityStateBitmask); } ConvertFwVersion(TmpFwVerString, pDimms[DimmIndex].FwVer.FwProduct, pDimms[DimmIndex].FwVer.FwRevision, pDimms[DimmIndex].FwVer.FwSecurityVersion, pDimms[DimmIndex].FwVer.FwBuild); ReturnCode = GetPreferredDimmIdAsString(pDimms[DimmIndex].DimmHandle, pDimms[DimmIndex].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); pDimmErrStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, CAPACITY_STR, pCapacityStr); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, HEALTH_STR, pHealthStr); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SECURITY_STR, pSecurityStr); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, FW_VER_STR, TmpFwVerString); if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_UID) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, pDimmErrStr); } else { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, DimmStr); } FREE_POOL_SAFE(pDimmErrStr); FREE_POOL_SAFE(pHealthStr); FREE_POOL_SAFE(pSecurityStr); FREE_POOL_SAFE(pCapacityStr); } } /** display detailed view **/ else { ShowAll = pDispOptions->AllOptionSet; if (pDispOptions->DisplayOptionSet) { gDisplayNulls = TRUE; pOriginalNullVal = gNullValueToDisplay; gNullValueToDisplay = L"Unsupported Field"; } /** show dimms from Initialized list **/ for (DimmIndex = 0; DimmIndex < DimmCount; DimmIndex++) { /** matching pid **/ if (DimmIdsNum > 0 && !ContainUint(pDimmIds, DimmIdsNum, pDimms[DimmIndex].DimmID)) { continue; } if (SocketsNum > 0 && !ContainUint(pSocketIds, SocketsNum, pDimms[DimmIndex].SocketId)) { continue; } PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_INDEX_PATH, DimmIndex); //Checking the FIS Version if ((pDimms[DimmIndex].FwVer.FwApiMajor >= 2) || (pDimms[DimmIndex].FwVer.FwApiMajor == 1 && pDimms[DimmIndex].FwVer.FwApiMinor >= 13)) { FIS_2_0 = TRUE; } /** always print the DimmID **/ ReturnCode = GetPreferredDimmIdAsString(pDimms[DimmIndex].DimmHandle, pDimms[DimmIndex].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_UID) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, UNKNOWN_ATTRIB_VAL); } else { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, DimmStr); } /** Capacity **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, CAPACITY_STR))) { ReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].Capacity, UnitsToDisplay, TRUE, &pCapacityStr); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, CAPACITY_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); } /** Security State **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SECURITY_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_SECURITY_INFO || MANAGEMENT_VALID_CONFIG != pDimms[DimmIndex].ManageabilityState) { pSecurityStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { pSecurityStr = SecurityStateBitmaskToString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].SecurityStateBitmask); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SECURITY_STR, pSecurityStr); FREE_POOL_SAFE(pSecurityStr); } /** SVN Downgrade Opt-In **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SVN_DOWNGRADE_OPT_IN_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_SVN_DOWNGRADE) { pSVNDowngradeStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { pSVNDowngradeStr = SVNDowngradeOptInToString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].SVNDowngradeOptIn); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SVN_DOWNGRADE_OPT_IN_STR, pSVNDowngradeStr); FREE_POOL_SAFE(pSVNDowngradeStr); } /** Secure Erase Policy Opt-In **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SEP_OPT_IN_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_SECURE_ERASE_POLICY) { pSecureErasePolicyStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { pSecureErasePolicyStr = SecureErasePolicyOptInToString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].SecureErasePolicyOptIn); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SEP_OPT_IN_STR, pSecureErasePolicyStr); FREE_POOL_SAFE(pSecureErasePolicyStr); } /** S3 Resume Opt-In **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, S3_RESUME_OPT_IN_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_S3RESUME) { pS3ResumeStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { pS3ResumeStr = S3ResumeOptInToString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].S3ResumeOptIn); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, S3_RESUME_OPT_IN_STR, pS3ResumeStr); FREE_POOL_SAFE(pS3ResumeStr); } /** FW Activate Opt-In **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, FW_ACTIVATE_OPT_IN_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_FW_ACTIVATE) { pFwActivateStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { pFwActivateStr = FwActivateOptInToString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].FwActivateOptIn); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, FW_ACTIVATE_OPT_IN_STR, pFwActivateStr); FREE_POOL_SAFE(pFwActivateStr); } /** Health State **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, HEALTH_STR))) { pHealthStr = HealthToString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].HealthState); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, HEALTH_STR, pHealthStr); FREE_POOL_SAFE(pHealthStr); } /** Health State Reason**/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, HEALTH_STATE_REASON_STR))) { ReturnCode = ConvertHealthStateReasonToHiiStr(gNvmDimmCliHiiHandle, pDimms[DimmIndex].HealthStatusReason, &pHealthStateReasonStr); if (pHealthStateReasonStr == NULL || EFI_ERROR(ReturnCode)) { goto Finish; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, HEALTH_STATE_REASON_STR, pHealthStateReasonStr); FREE_POOL_SAFE(pHealthStateReasonStr); } /** FwVersion **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, FW_VER_STR))) { ConvertFwVersion(TmpFwVerString, pDimms[DimmIndex].FwVer.FwProduct, pDimms[DimmIndex].FwVer.FwRevision, pDimms[DimmIndex].FwVer.FwSecurityVersion, pDimms[DimmIndex].FwVer.FwBuild); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, FW_VER_STR, TmpFwVerString); } /** FwApiVersion **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, FW_API_VER_STR))) { ConvertFwApiVersion(TmpFwVerString, pDimms[DimmIndex].FwVer.FwApiMajor, pDimms[DimmIndex].FwVer.FwApiMinor); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, FW_API_VER_STR, TmpFwVerString); } /** FwActiveApiVersion **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, FW_ACTIVE_API_VER_STR))) { ConvertFwApiVersion(TmpFwVerString, pDimms[DimmIndex].FwActiveApiVersionMajor, pDimms[DimmIndex].FwActiveApiVersionMinor); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, FW_ACTIVE_API_VER_STR, TmpFwVerString); } /** InterfaceFormatCode **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, INTERFACE_FORMAT_CODE_STR))) { if (pDimms[DimmIndex].InterfaceFormatCodeNum <= MAX_IFC_NUM) { CHAR16 *tmpIfc = NULL; for (Index2 = 0; Index2 < pDimms[DimmIndex].InterfaceFormatCodeNum; Index2++) { if (pDimms[DimmIndex].InterfaceFormatCode[Index2] == DCPMM_FMT_CODE_APP_DIRECT) { ByteAddressable = TRUE; } } if (ByteAddressable) { tmpIfc = CatSPrintClean(tmpIfc, FORMAT_HEX L" ", DCPMM_FMT_CODE_APP_DIRECT); tmpIfc = CatSPrintClean(tmpIfc, FORMAT_CODE_APP_DIRECT_STR); } if (pDimms[DimmIndex].InterfaceFormatCodeNum > 1) { tmpIfc = CatSPrintClean(tmpIfc, L", "); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, INTERFACE_FORMAT_CODE_STR, tmpIfc); FREE_POOL_SAFE(tmpIfc); } } /** Manageability **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MANAGEABILITY_STR))) { pManageabilityStr = ManageabilityToString(pDimms[DimmIndex].ManageabilityState); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MANAGEABILITY_STR, pManageabilityStr); FREE_POOL_SAFE(pManageabilityStr); } /** PopulationViolation **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, POPULATION_VIOLATION_STR))) { pPopulationViolationStr = PopulationViolationToString(pDimms[DimmIndex].IsInPopulationViolation); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, POPULATION_VIOLATION_STR, pPopulationViolationStr); FREE_POOL_SAFE(pPopulationViolationStr); } /** PhysicalID **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, PHYSICAL_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PHYSICAL_ID_STR, FORMAT_HEX, pDimms[DimmIndex].DimmID); } /** DimmHandle **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, DIMM_HANDLE_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DIMM_HANDLE_STR, FORMAT_HEX, pDimms[DimmIndex].DimmHandle); } /** DimmUID **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, DIMM_UID_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_UID) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_UID_STR, UNKNOWN_ATTRIB_VAL); } else { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_UID_STR, pDimms[DimmIndex].DimmUid); } } /** SocketId **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SOCKET_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SOCKET_ID_STR, FORMAT_HEX, pDimms[DimmIndex].SocketId); } /** MemoryControllerId **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MEMORY_CONTROLLER_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MEMORY_CONTROLLER_STR, FORMAT_HEX, pDimms[DimmIndex].ImcId); } /** ChannelID **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, CHANNEL_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, CHANNEL_ID_STR, FORMAT_HEX, pDimms[DimmIndex].ChannelId); } /** ChannelPos **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, CHANNEL_POS_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, CHANNEL_POS_STR, FORMAT_INT32, pDimms[DimmIndex].ChannelPos); } /** MemoryType **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MEMORY_TYPE_STR))) { pAttributeStr = MemoryTypeToStr(pDimms[DimmIndex].MemoryType); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEMORY_TYPE_STR, pAttributeStr); FREE_POOL_SAFE(pAttributeStr); } /** ManufacturerStr **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MANUFACTURER_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MANUFACTURER_STR, pDimms[DimmIndex].ManufacturerStr); } /** VendorId **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, VENDOR_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, VENDOR_ID_STR, FORMAT_HEX, EndianSwapUint16(pDimms[DimmIndex].VendorId)); } /** DeviceId **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, DEVICE_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DEVICE_ID_STR, FORMAT_HEX, EndianSwapUint16(pDimms[DimmIndex].DeviceId)); } /** RevisionId **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, REVISION_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, REVISION_ID_STR, FORMAT_HEX, pDimms[DimmIndex].Rid); } /** SubsystemVendorId **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SUBSYSTEM_VENDOR_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SUBSYSTEM_VENDOR_ID_STR, FORMAT_HEX, EndianSwapUint16(pDimms[DimmIndex].SubsystemVendorId)); } /** SubsystemDeviceId **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SUBSYSTEM_DEVICE_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SUBSYSTEM_DEVICE_ID_STR, FORMAT_HEX, pDimms[DimmIndex].SubsystemDeviceId); } /** SubsystemRevisionId **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SUBSYSTEM_REVISION_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SUBSYSTEM_REVISION_ID_STR, FORMAT_HEX, pDimms[DimmIndex].SubsystemRid); } /** DeviceLocator **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, DEVICE_LOCATOR_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DEVICE_LOCATOR_STR, pDimms[DimmIndex].DeviceLocator); } /** ManufacturingInfoValid **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MANUFACTURING_INFO_VALID))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MANUFACTURING_INFO_VALID, FORMAT_INT32, pDimms[DimmIndex].ManufacturingInfoValid); } /** ManufacturingLocation **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MANUFACTURING_LOCATION))) { if (pDimms[DimmIndex].ManufacturingInfoValid) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MANUFACTURING_LOCATION, FORMAT_HEX_PREFIX FORMAT_UINT8_HEX, pDimms[DimmIndex].ManufacturingLocation); } else { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MANUFACTURING_LOCATION, NA_STR); } } /** ManufacturingDate **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MANUFACTURING_DATE))) { if (pDimms[DimmIndex].ManufacturingInfoValid) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MANUFACTURING_DATE, FORMAT_SHOW_DIMM_MANU_DATE, pDimms[DimmIndex].ManufacturingDate & 0xFF, (pDimms[DimmIndex].ManufacturingDate >> 8) & 0xFF); } else { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MANUFACTURING_DATE, NA_STR); } } /** SerialNumber **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SERIAL_NUMBER_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SERIAL_NUMBER_STR, FORMAT_HEX_PREFIX FORMAT_UINT32_HEX, EndianSwapUint32(pDimms[DimmIndex].SerialNumber)); } /** PartNumber **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, PART_NUMBER_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, PART_NUMBER_STR, pDimms[DimmIndex].PartNumber); } /** BankLabel **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, BANK_LABEL_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, BANK_LABEL_STR, pDimms[DimmIndex].BankLabel); } /** DataWidth **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, DATA_WIDTH_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DATA_WIDTH_STR, FORMAT_INT32 L" " BYTE_STR, pDimms[DimmIndex].DataWidth); } /** TotalWidth **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, TOTAL_WIDTH_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, TOTAL_WIDTH_STR, FORMAT_INT32 L" " BYTE_STR, pDimms[DimmIndex].TotalWidth); } /** Speed **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SPEED_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SPEED_STR, FORMAT_INT32 L" " MEGA_TRANSFERS_PER_SEC_STR, pDimms[DimmIndex].Speed); } /** FormFactor **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, FORM_FACTOR_STR))) { pFormFactorStr = FormFactorToString(pDimms[DimmIndex].FormFactor); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, FORM_FACTOR_STR, pFormFactorStr); FREE_POOL_SAFE(pFormFactorStr); } /** If Dimm is Manageable print rest of the attributes **/ if (pDimms[DimmIndex].ManageabilityState) { /** ManufacturerId **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MANUFACTURER_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MANUFACTURER_ID_STR, FORMAT_HEX, EndianSwapUint16(pDimms[DimmIndex].ManufacturerId)); } /** ControllerRevisionId **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, CONTROLLER_REVISION_ID_STR))) { pSteppingStr = ControllerRidToStr(pDimms[DimmIndex].ControllerRid, pDimms[DimmIndex].SubsystemDeviceId); if (pSteppingStr != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, CONTROLLER_REVISION_ID_STR, pSteppingStr); FREE_POOL_SAFE(pSteppingStr); } } /** VolatileCapacity **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MEMORY_MODE_CAPACITY_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_CAPACITY) { pCapacityStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].VolatileCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEMORY_MODE_CAPACITY_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); } /** AppDirectCapacity **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, APPDIRECT_MODE_CAPACITY_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_CAPACITY) { pCapacityStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].AppDirectCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, APPDIRECT_MODE_CAPACITY_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); } /** UnconfiguredCapacity **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, UNCONFIGURED_CAPACITY_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_CAPACITY) { pCapacityStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].UnconfiguredCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, UNCONFIGURED_CAPACITY_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); } /** InaccessibleCapacity **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, INACCESSIBLE_CAPACITY_STR))) { KEEP_ERROR(ReturnCode, TempReturnCode); if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_CAPACITY) { pCapacityStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].InaccessibleCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, INACCESSIBLE_CAPACITY_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); } /** ReservedCapacity **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, RESERVED_CAPACITY_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_CAPACITY) { pCapacityStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pDimms[DimmIndex].ReservedCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, RESERVED_CAPACITY_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); } /** PackageSparingCapable **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, PACKAGE_SPARING_CAPABLE_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PACKAGE_SPARING_CAPABLE_STR, FORMAT_INT32, pDimms[DimmIndex].PackageSparingCapable); } if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_PACKAGE_SPARING) { /** PackageSparingEnabled **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, PACKAGE_SPARING_ENABLED_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, PACKAGE_SPARING_ENABLED_STR, UNKNOWN_ATTRIB_VAL); } /** PackageSparesAvailable **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, PACKAGE_SPARES_AVAILABLE_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, PACKAGE_SPARES_AVAILABLE_STR, UNKNOWN_ATTRIB_VAL); } } else { /** PackageSparingEnabled **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, PACKAGE_SPARING_ENABLED_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PACKAGE_SPARING_ENABLED_STR, FORMAT_INT32, pDimms[DimmIndex].PackageSparingEnabled); } /** PackageSparesAvailable **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, PACKAGE_SPARES_AVAILABLE_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PACKAGE_SPARES_AVAILABLE_STR, FORMAT_INT32, pDimms[DimmIndex].PackageSparesAvailable); } } /** IsNew **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, IS_NEW_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, IS_NEW_STR, FORMAT_INT32, pDimms[DimmIndex].IsNew); } /** AveragePowerReportingTimeConstant (FIS 2.1 and higher) **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, AVG_PWR_REPORTING_TIME_CONSTANT))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].AvgPowerReportingTimeConstant, FORMAT_UINT64 L" " TIME_MSR_MS); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, AVG_PWR_REPORTING_TIME_CONSTANT, pStr); FREE_POOL_SAFE(pStr); } if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_VIRAL_POLICY) { /** ViralPolicyEnable **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, VIRAL_POLICY_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, VIRAL_POLICY_STR, UNKNOWN_ATTRIB_VAL); } /** ViralStatus **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, VIRAL_STATE_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, VIRAL_STATE_STR, UNKNOWN_ATTRIB_VAL); } } else { /** ViralPolicyEnable **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, VIRAL_POLICY_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, VIRAL_POLICY_STR, FORMAT_INT32, pDimms[DimmIndex].ViralPolicyEnable); } /** ViralStatus **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, VIRAL_STATE_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, VIRAL_STATE_STR, FORMAT_INT32, pDimms[DimmIndex].ViralStatus); } } /** PeakPowerBudget **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, PEAK_POWER_BUDGET_STR))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].PeakPowerBudget, FORMAT_INT32 L" " MILI_WATT_STR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PEAK_POWER_BUDGET_STR, pStr); FREE_POOL_SAFE(pStr); } /** AvgPowerLimit **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, AVG_POWER_LIMIT_STR))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].AvgPowerLimit, FORMAT_INT32 L" " MILI_WATT_STR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, AVG_POWER_LIMIT_STR, pStr); FREE_POOL_SAFE(pStr); } /** 2.1+: MemoryBandwidthBoostFeature **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MEMORY_BANDWIDTH_BOOST_FEATURE_STR))) { if ((2 == pDimms[DimmIndex].FwVer.FwApiMajor && 1 <= pDimms[DimmIndex].FwVer.FwApiMinor) || 3 <= pDimms[DimmIndex].FwVer.FwApiMajor) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].MemoryBandwidthBoostFeature, FORMAT_HEX_NOWIDTH); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MEMORY_BANDWIDTH_BOOST_FEATURE_STR, pStr); FREE_POOL_SAFE(pStr); } } /** 2.1+: MemoryBandwidthBoostMaxPowerLimit **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MEMORY_BANDWIDTH_BOOST_MAX_POWER_LIMIT_STR))) { if ((2 == pDimms[DimmIndex].FwVer.FwApiMajor && 1 <= pDimms[DimmIndex].FwVer.FwApiMinor) || 3 <= pDimms[DimmIndex].FwVer.FwApiMajor) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].MemoryBandwidthBoostMaxPowerLimit, FORMAT_INT32 L" " MILI_WATT_STR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MEMORY_BANDWIDTH_BOOST_MAX_POWER_LIMIT_STR, pStr); FREE_POOL_SAFE(pStr); } } /** MemoryBandwidthBoostAveragePowerTimeConstant **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT_STR))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].MemoryBandwidthBoostAveragePowerTimeConstant, FORMAT_UINT64 L" " TIME_MSR_MS); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT_STR, pStr); FREE_POOL_SAFE(pStr); } /** MaxAveragePowerLimit **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MAX_AVG_POWER_LIMIT_STR))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].MaxAveragePowerLimit, FORMAT_INT32 L" " MILI_WATT_STR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MAX_AVG_POWER_LIMIT_STR, pStr); FREE_POOL_SAFE(pStr); } /** MaxMemoryBandwidthBoostMaxPowerLimit **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MAX_MEMORY_BANDWIDTH_BOOST_MAX_POWER_LIMIT))) { if ((2 == pDimms[DimmIndex].FwVer.FwApiMajor && 0 <= pDimms[DimmIndex].FwVer.FwApiMinor) || 3 <= pDimms[DimmIndex].FwVer.FwApiMajor) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].MaxMemoryBandwidthBoostMaxPowerLimit, FORMAT_INT32 L" " MILI_WATT_STR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MAX_MEMORY_BANDWIDTH_BOOST_MAX_POWER_LIMIT, pStr); FREE_POOL_SAFE(pStr); } } /** MaxMemoryBandwidthBoostAveragePowerTimeConstant **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MAX_MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].MaxMemoryBandwidthBoostAveragePowerTimeConstant, FORMAT_INT32 L" " TIME_MSR_MS); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MAX_MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT, pStr); FREE_POOL_SAFE(pStr); } /** MemoryBandwidthBoostAveragePowerTimeConstantStep **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT_STEP))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].MemoryBandwidthBoostAveragePowerTimeConstantStep, FORMAT_INT32 L" " TIME_MSR_MS); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT_STEP, pStr); FREE_POOL_SAFE(pStr); } /** MaxAveragePowerReportingTimeConstant **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MAX_AVERAGE_POWER_REPORTING_TIME_CONSTANT))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].MaxAveragePowerReportingTimeConstant, FORMAT_INT32 L" " TIME_MSR_MS); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MAX_AVERAGE_POWER_REPORTING_TIME_CONSTANT, pStr); FREE_POOL_SAFE(pStr); } /** AveragePowerReportingTimeConstantStep **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, AVERAGE_POWER_REPORTING_TIME_CONSTANT_STEP))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].AveragePowerReportingTimeConstantStep, FORMAT_INT32 L" " TIME_MSR_MS); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, AVERAGE_POWER_REPORTING_TIME_CONSTANT_STEP, pStr); FREE_POOL_SAFE(pStr); } /** DcpmmAveragePower **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, DCPMM_AVERAGE_POWER_STR))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].DcpmmAveragePower, FORMAT_INT32 L" " MILI_WATT_STR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DCPMM_AVERAGE_POWER_STR, pStr); FREE_POOL_SAFE(pStr); } /** AveragePower12V **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, AVERAGE_12V_POWER_STR))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].AveragePower12V, FORMAT_INT32 L" " MILI_WATT_STR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, AVERAGE_12V_POWER_STR, pStr); FREE_POOL_SAFE(pStr); } /** AveragePower1_2V **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, AVERAGE_1_2V_POWER_STR))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].AveragePower1_2V, FORMAT_INT32 L" " MILI_WATT_STR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, AVERAGE_1_2V_POWER_STR, pStr); FREE_POOL_SAFE(pStr); } /** LatchedLastShutdownStatusDetails **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, LATCHED_LAST_SHUTDOWN_STATUS_STR))) { LatchedLastShutdownStatusDetails.AsUint32 = pDimms[DimmIndex].LatchedLastShutdownStatusDetails; if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_SMART_AND_HEALTH) { pAttributeStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { pAttributeStr = LastShutdownStatusToStr(LatchedLastShutdownStatusDetails, pDimms[DimmIndex].FwVer); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, LATCHED_LAST_SHUTDOWN_STATUS_STR, pAttributeStr); FREE_POOL_SAFE(pAttributeStr); } /** UnlatchedLastShutdownStatusDetails **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, UNLATCHED_LAST_SHUTDOWN_STATUS_STR))) { UnlatchedLastShutdownStatusDetails.AsUint32 = pDimms[DimmIndex].UnlatchedLastShutdownStatusDetails; if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_SMART_AND_HEALTH) { pAttributeStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { pAttributeStr = LastShutdownStatusToStr(UnlatchedLastShutdownStatusDetails, pDimms[DimmIndex].FwVer); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, UNLATCHED_LAST_SHUTDOWN_STATUS_STR, pAttributeStr); FREE_POOL_SAFE(pAttributeStr); } /** ThermalThrottlePerformanceLossPrct **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, THERMAL_THROTTLE_LOSS_STR))) { if ((pDimms[DimmIndex].FwVer.FwApiMajor == 0x2 && pDimms[DimmIndex].FwVer.FwApiMinor >= 0x1) || (pDimms[DimmIndex].FwVer.FwApiMajor >= 0x3)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, THERMAL_THROTTLE_LOSS_STR, FORMAT_UINT32, pDimms[DimmIndex].ThermalThrottlePerformanceLossPrct); } else { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, THERMAL_THROTTLE_LOSS_STR, NA_STR); } } /** LastShutdownTime **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, LAST_SHUTDOWN_TIME_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_SMART_AND_HEALTH) { pAttributeStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { pAttributeStr = GetTimeFormatString(pDimms[DimmIndex].LastShutdownTime, TRUE); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, LAST_SHUTDOWN_TIME_STR, pAttributeStr); FREE_POOL_SAFE(pAttributeStr); } /** ModesSupported **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MODES_SUPPORTED_STR))) { pAttributeStr = ModesSupportedToStr(pDimms[DimmIndex].ModesSupported); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MODES_SUPPORTED_STR, pAttributeStr); FREE_POOL_SAFE(pAttributeStr); } /** SecurityCapabilities **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SECURITY_CAPABILITIES_STR))) { pAttributeStr = SecurityCapabilitiesToStr(pDimms[DimmIndex].SecurityCapabilities); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SECURITY_CAPABILITIES_STR, pAttributeStr); FREE_POOL_SAFE(pAttributeStr); } /** MasterPassphraseEnabled **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MASTER_PASS_ENABLED_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MASTER_PASS_ENABLED_STR, FORMAT_INT32, pDimms[DimmIndex].MasterPassphraseEnabled); } /** ConfigurationStatus **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, DIMM_CONFIG_STATUS_STR))) { pAttributeStr = mppAllowedShowDimmsConfigStatuses[pDimms[DimmIndex].ConfigStatus]; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_CONFIG_STATUS_STR, pAttributeStr); } /** SKUViolation **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SKU_VIOLATION_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SKU_VIOLATION_STR, FORMAT_INT32, pDimms[DimmIndex].SKUViolation); } /** ARSStatus **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ARS_STATUS_STR))) { pAttributeStr = LongOpStatusToStr(gNvmDimmCliHiiHandle, pDimms[DimmIndex].ARSStatus); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ARS_STATUS_STR, pAttributeStr); FREE_POOL_SAFE(pAttributeStr); } /** OverwriteDimmStatus **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, OVERWRITE_STATUS_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_OVERWRITE_STATUS) { pAttributeStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { pAttributeStr = LongOpStatusToStr(gNvmDimmCliHiiHandle, pDimms[DimmIndex].OverwriteDimmStatus); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, OVERWRITE_STATUS_STR, pAttributeStr); FREE_POOL_SAFE(pAttributeStr); } /** AitDramEnabled **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, AIT_DRAM_ENABLED_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_SMART_AND_HEALTH) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, AIT_DRAM_ENABLED_STR, UNKNOWN_ATTRIB_VAL); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, AIT_DRAM_ENABLED_STR, FORMAT_INT32, pDimms[DimmIndex].AitDramEnabled); } } /** Boot Status and/or Boot Status Register **/ if (ShowAll || (pDispOptions->DisplayOptionSet && (ContainsValue(pDispOptions->pDisplayValues, BOOT_STATUS_STR) || ContainsValue(pDispOptions->pDisplayValues, BOOT_STATUS_REGISTER_STR)))) { pNvmDimmConfigProtocol->GetBSRAndBootStatusBitMask(pNvmDimmConfigProtocol, pDimms[DimmIndex].DimmID, &BootStatusRegister, &BootStatusBitMask); pAttributeStr = BootStatusBitmaskToStr(gNvmDimmCliHiiHandle, BootStatusBitMask); if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, BOOT_STATUS_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, BOOT_STATUS_STR, pAttributeStr); FREE_POOL_SAFE(pAttributeStr); } if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, BOOT_STATUS_REGISTER_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, BOOT_STATUS_REGISTER_STR, FORMAT_HEX_PREFIX FORMAT_UINT32_HEX L"_" FORMAT_UINT32_HEX, ((BootStatusRegister >> 32) & 0xFFFFFFFF), (BootStatusRegister & 0xFFFFFFFF)); } } if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_LATCH_SYSTEM_SHUTDOWN_STATE) { /** LatchSystemShutdownState **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, LATCH_SYSTEM_SHUTDOWN_STATE_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, LATCH_SYSTEM_SHUTDOWN_STATE_STR, UNKNOWN_ATTRIB_VAL); } /** PreviousPowerCycleLatchSystemShutdownState **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, PREV_PWR_CYCLE_LATCH_SYSTEM_SHUTDOWN_STATE_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, PREV_PWR_CYCLE_LATCH_SYSTEM_SHUTDOWN_STATE_STR, UNKNOWN_ATTRIB_VAL); } } else { /** LatchSystemShutdownState **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, LATCH_SYSTEM_SHUTDOWN_STATE_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, LATCH_SYSTEM_SHUTDOWN_STATE_STR, FORMAT_INT32, pDimms[DimmIndex].LatchSystemShutdownState); } /** PreviousPowerCycleLatchSystemShutdownState **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, PREV_PWR_CYCLE_LATCH_SYSTEM_SHUTDOWN_STATE_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PREV_PWR_CYCLE_LATCH_SYSTEM_SHUTDOWN_STATE_STR, FORMAT_INT32, pDimms[DimmIndex].PrevPwrCycleLatchSystemShutdownState); } } /** ExtendedAdrEnabled **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, EXTENDED_ADR_ENABLED_STR))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].ExtendedAdrEnabled, FORMAT_INT32); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, EXTENDED_ADR_ENABLED_STR, pStr); FREE_POOL_SAFE(pStr); } /** PpcExtendedAdrEnabled **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, PPC_EXTENDED_ADR_ENABLED_STR))) { pStr = ConvertDimmInfoAttribToString((VOID*)&pDimms[DimmIndex].PrevPwrCycleExtendedAdrEnabled, FORMAT_INT32); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PPC_EXTENDED_ADR_ENABLED_STR, pStr); FREE_POOL_SAFE(pStr); } if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_MEM_INFO_PAGE) { /** ErrorInjectionEnabled **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ERROR_INJECT_ENABLED_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_INJECT_ENABLED_STR, UNKNOWN_ATTRIB_VAL); } /** MediaTemperatureInjectionEnabled **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MEDIA_TEMP_INJ_ENABLED_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEDIA_TEMP_INJ_ENABLED_STR, UNKNOWN_ATTRIB_VAL); } /** SoftwareTriggersEnabled **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SW_TRIGGERS_ENABLED_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SW_TRIGGERS_ENABLED_STR, UNKNOWN_ATTRIB_VAL); } /** SoftwareTriggersEnabledDetails **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SW_TRIGGER_ENABLED_DETAILS_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SW_TRIGGER_ENABLED_DETAILS_STR, UNKNOWN_ATTRIB_VAL); } /** PoisonErrorInjectionsCounter **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, POISON_ERR_INJ_CTR_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, POISON_ERR_INJ_CTR_STR, UNKNOWN_ATTRIB_VAL); } /** PoisonErrorClearCounter **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, POISON_ERR_CLR_CTR_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, POISON_ERR_CLR_CTR_STR, UNKNOWN_ATTRIB_VAL); } /** MediaTemperatureInjectionsCounter **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MEDIA_TEMP_INJ_CTR_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEDIA_TEMP_INJ_CTR_STR, UNKNOWN_ATTRIB_VAL); } /** SoftwareTriggersCounter **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SW_TRIGGER_CTR_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SW_TRIGGER_CTR_STR, UNKNOWN_ATTRIB_VAL); } } else { /** ErrorInjectionEnabled **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ERROR_INJECT_ENABLED_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, ERROR_INJECT_ENABLED_STR, FORMAT_INT32, pDimms[DimmIndex].ErrorInjectionEnabled); } /** MediaTemperatureInjectionEnabled **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MEDIA_TEMP_INJ_ENABLED_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MEDIA_TEMP_INJ_ENABLED_STR, FORMAT_INT32, pDimms[DimmIndex].MediaTemperatureInjectionEnabled); } /** SoftwareTriggersEnabled **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SW_TRIGGERS_ENABLED_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SW_TRIGGERS_ENABLED_STR, FORMAT_INT32, pDimms[DimmIndex].SoftwareTriggersEnabled); } /** SoftwareTriggersEnabledDetails **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SW_TRIGGER_ENABLED_DETAILS_STR))) { pAttributeStr = SoftwareTriggersEnabledToStr(pDimms[DimmIndex].SoftwareTriggersEnabledDetails); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SW_TRIGGER_ENABLED_DETAILS_STR, pAttributeStr); FREE_POOL_SAFE(pAttributeStr); } /** PoisonErrorInjectionsCounter **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, POISON_ERR_INJ_CTR_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, POISON_ERR_INJ_CTR_STR, FORMAT_INT32, pDimms[DimmIndex].PoisonErrorInjectionsCounter); } /** PoisonErrorClearCounter **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, POISON_ERR_CLR_CTR_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, POISON_ERR_CLR_CTR_STR, FORMAT_INT32, pDimms[DimmIndex].PoisonErrorClearCounter); } /** MediaTemperatureInjectionsCounter **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MEDIA_TEMP_INJ_CTR_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MEDIA_TEMP_INJ_CTR_STR, FORMAT_INT32, pDimms[DimmIndex].MediaTemperatureInjectionsCounter); } /** SoftwareTriggersCounter **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SW_TRIGGER_CTR_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SW_TRIGGER_CTR_STR, FORMAT_INT32, pDimms[DimmIndex].SoftwareTriggersCounter); } if (!FIS_2_0) { /** Max Controller Temperature **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MAX_CONTROLLER_TEMPERATURE_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MAX_CONTROLLER_TEMPERATURE_STR, NOT_APPLICABLE_SHORT_STR); } if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MAX_MEDIA_TEMPERATURE_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MAX_MEDIA_TEMPERATURE_STR, NOT_APPLICABLE_SHORT_STR); } } else { /** Max Controller Temperature **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MAX_CONTROLLER_TEMPERATURE_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_SMART_AND_HEALTH) { pAttributeStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MAX_CONTROLLER_TEMPERATURE_STR, FORMAT_UINT32 L" " TEMPERATURE_MSR, pDimms[DimmIndex].MaxControllerTemperature); } FREE_POOL_SAFE(pAttributeStr); } /** Max Media Temperature **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MAX_MEDIA_TEMPERATURE_STR))) { if (pDimms[DimmIndex].ErrorMask & DIMM_INFO_ERROR_SMART_AND_HEALTH) { pAttributeStr = CatSPrint(NULL, FORMAT_STR, UNKNOWN_ATTRIB_VAL); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MAX_MEDIA_TEMPERATURE_STR, FORMAT_UINT32 L" " TEMPERATURE_MSR, pDimms[DimmIndex].MaxMediaTemperature); } FREE_POOL_SAFE(pAttributeStr); } } } if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MIXED_SKU_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MIXED_SKU_STR, (IsMixedSku == TRUE) ? L"1" : L"0"); } // Pass FIS version into determine FIPS string (print N/A for older versions) if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, FIPS_MODE_STATUS_STR))) { // Get FIPS status ReturnCode = pNvmDimmConfigProtocol->GetFIPSMode(pNvmDimmConfigProtocol, pDimms[DimmIndex].DimmID, &FIPSMode, pCommandStatus); pStr = ConvertFIPSModeToString(gNvmDimmCliHiiHandle, FIPSMode, pDimms[DimmIndex].FwVer, ReturnCode); // Overwrite ReturnCode since we don't care if it failed ReturnCode = EFI_SUCCESS; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, FIPS_MODE_STATUS_STR, pStr); FREE_POOL_SAFE(pStr); } } else { // Set certain fields to N/A if NVDIMM is unmanageable for (Index3 = 0; Index3 < ALLOWED_DISP_VALUES_COUNT(pOnlyManageableAllowedDisplayValues); Index3++) { if (ShowAll || ContainsValue(pDispOptions->pDisplayValues, pOnlyManageableAllowedDisplayValues[Index3])) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, pOnlyManageableAllowedDisplayValues[Index3], NA_STR); } } } } } if (FALSE == ShowAll && gNullValuesEncounteredForDisplay > 0) { if (ReturnCode == EFI_SUCCESS) { ReturnCode = EFI_INVALID_PARAMETER; } PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_SOME_VALUES_NOT_SUPPORTED); } //Specify table attributes PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowDimmDataSetAttribs); Finish: gDisplayNulls = FALSE; gNullValuesEncounteredForDisplay = 0; gNullValueToDisplay = pOriginalNullVal; PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pPath); FREE_CMD_DISPLAY_OPTIONS_SAFE(pDispOptions); FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pAllDimms); FREE_POOL_SAFE(pDimmIds); FreeCommandStatus(&pCommandStatus); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Convert manageability state to a string **/ STATIC CHAR16* ManageabilityToString( IN UINT8 ManageabilityState ) { CHAR16 *pManageabilityString = NULL; switch (ManageabilityState) { case MANAGEMENT_VALID_CONFIG: pManageabilityString = CatSPrint(NULL, FORMAT_STR, L"Manageable"); break; case MANAGEMENT_INVALID_CONFIG: default: pManageabilityString = CatSPrint(NULL, FORMAT_STR, L"Unmanageable"); break; } return pManageabilityString; } /** Convert population violation state to a string **/ STATIC CHAR16* PopulationViolationToString( IN BOOLEAN IsInPopulationViolation ) { CHAR16 *pPopulationViolationString = NULL; pPopulationViolationString = CatSPrint(NULL, FORMAT_STR, IsInPopulationViolation ? L"Yes" : L"No"); return pPopulationViolationString; } /** Convert type to string **/ STATIC CHAR16* FormFactorToString( IN UINT8 FormFactor ) { CHAR16 *pFormFactorStr = NULL; switch (FormFactor) { case MemoryFormFactorDimm: pFormFactorStr = CatSPrint(NULL, FORMAT_STR, L"DIMM"); break; case MemoryFormFactorSodimm: pFormFactorStr = CatSPrint(NULL, FORMAT_STR, L"SODIMM"); break; default: pFormFactorStr = CatSPrint(NULL, FORMAT_STR, L"Other"); break; } return pFormFactorStr; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowDimmsCommand.h000066400000000000000000000156061440615110200213010ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_DIMMS_COMMAND_H_ #define _SHOW_DIMMS_COMMAND_H_ #include "CommandParser.h" #define UNKNOWN_ATTRIB_VAL L"Unknown" /** show -dimm display options some of common display options are in CommandParser.h **/ #define FW_VER_STR L"FWVersion" #define FW_API_VER_STR L"FWAPIVersion" #define FW_ACTIVE_API_VER_STR L"FWActiveAPIVersion" #define INTERFACE_FORMAT_CODE_STR L"InterfaceFormatCode" #define CAPACITY_STR L"Capacity" #define MANAGEABILITY_STR L"ManageabilityState" #define POPULATION_VIOLATION_STR L"PopulationViolation" #define MANAGEABILITY_SHORT_STR L"Manageability" #define HEALTH_STR L"HealthState" #define HEALTH_STATE_REASON_STR L"HealthStateReason" #define HEALTH_SHORT_STR L"Health" #define SECURITY_STR L"LockState" #define SVN_DOWNGRADE_OPT_IN_STR L"SVNDowngrade" #define SEP_OPT_IN_STR L"SecureErasePolicy" #define S3_RESUME_OPT_IN_STR L"S3ResumeOptIn" #define FW_ACTIVATE_OPT_IN_STR L"FwActivateOptIn" #define SECURITY_SKU_ID_STR L"SecuritySKUId" #define SECURITY_SHORT_STR L"Security" #define MASTER_STR L"Master" #define TYPE_STR L"Type" #define FORM_FACTOR_STR L"FormFactor" #define VENDOR_ID_STR L"VendorID" #define MANUFACTURER_ID_STR L"ManufacturerID" #define MANUFACTURER_STR L"Manufacturer" #define DEVICE_ID_STR L"DeviceID" #define REVISION_ID_STR L"RevisionID" #define SUBSYSTEM_VENDOR_ID_STR L"SubsystemVendorID" #define SUBSYSTEM_DEVICE_ID_STR L"SubsystemDeviceID" #define SUBSYSTEM_REVISION_ID_STR L"SubsystemRevisionID" #define CONTROLLER_REVISION_ID_STR L"ControllerRevisionID" #define MANUFACTURING_INFO_VALID L"ManufacturingInfoValid" #define MANUFACTURING_LOCATION L"ManufacturingLocation" #define MANUFACTURING_DATE L"ManufacturingDate" #define PART_NUMBER_STR L"PartNumber" #define SERIAL_NUMBER_STR L"SerialNumber" #define BOOT_STATUS_REGISTER_STR L"BootStatusRegister" #define DEVICE_LOCATOR_STR L"DeviceLocator" #define MEMORY_CONTROLLER_STR L"MemControllerID" #define CHANNEL_ID_STR L"ChannelID" #define SLOT_ID_STR L"SlotID" #define CHANNEL_POS_STR L"ChannelPos" #define DATA_WIDTH_STR L"DataWidth" #define TOTAL_WIDTH_STR L"TotalWidth" #define SPEED_STR L"Speed" #define MEMORY_MODE_CAPACITY_STR L"MemoryCapacity" #define APPDIRECT_MODE_CAPACITY_STR L"AppDirectCapacity" #define UNCONFIGURED_CAPACITY_STR L"UnconfiguredCapacity" #define RESERVED_CAPACITY_STR L"ReservedCapacity" #define PACKAGE_SPARING_ENABLED_STR L"PackageSparingEnabled" #define PACKAGE_SPARING_CAPABLE_STR L"PackageSparingCapable" #define PACKAGE_SPARES_AVAILABLE_STR L"PackageSparesAvailable" #define IS_NEW_STR L"IsNew" #define IN_VOLATILE_CAPACITY_STR L"InaccessibleVolatileCapacity" #define IN_PERSISTENT_CAPACITY_STR L"InaccessiblePersistentCapacity" #define BANK_LABEL_STR L"BankLabel" #define MEMORY_TYPE_STR L"MemoryType" #define PEAK_POWER_BUDGET_STR L"PeakPowerBudget" #define AVG_POWER_LIMIT_STR L"AvgPowerLimit" #define AVG_POWER_TIME_CONSTANT_STR L"AvgPowerTimeConstant" #define MEMORY_BANDWIDTH_BOOST_FEATURE_STR L"MemoryBandwidthBoostFeature" #define MEMORY_BANDWIDTH_BOOST_MAX_POWER_LIMIT_STR L"MemoryBandwidthBoostMaxPowerLimit" #define MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT_STR L"MemoryBandwidthBoostAveragePowerTimeConstant" #define LATCHED_LAST_SHUTDOWN_STATUS_STR L"LatchedLastShutdownStatus" #define UNLATCHED_LAST_SHUTDOWN_STATUS_STR L"UnlatchedLastShutdownStatus" #define THERMAL_THROTTLE_LOSS_STR L"ThermalThrottleLossPercent" #define LAST_SHUTDOWN_TIME_STR L"LastShutdownTime" #define PHYSICAL_ID_STR L"PhysicalID" #define DIMM_HANDLE_STR L"DimmHandle" #define DIMM_UID_STR L"DimmUID" #define MODES_SUPPORTED_STR L"ModesSupported" #define SECURITY_CAPABILITIES_STR L"SecurityCapabilities" #define MASTER_PASS_ENABLED_STR L"MasterPassphraseEnabled" #define DIMM_CONFIG_STATUS_STR L"ConfigurationStatus" #define SKU_VIOLATION_STR L"SKUViolation" #define ARS_STATUS_STR L"ARSStatus" #define OVERWRITE_STATUS_STR L"OverwriteStatus" #define RESERVED_CAPACITY_STR L"ReservedCapacity" #define INACCESSIBLE_CAPACITY_STR L"InaccessibleCapacity" #define VIRAL_POLICY_STR L"ViralPolicy" #define VIRAL_STATE_STR L"ViralState" #define AIT_DRAM_ENABLED_STR L"AitDramEnabled" #define BOOT_STATUS_STR L"BootStatus" #define ERROR_INJECT_ENABLED_STR L"ErrorInjectionEnabled" #define MEDIA_TEMP_INJ_ENABLED_STR L"MediaTemperatureInjectionEnabled" #define SW_TRIGGERS_ENABLED_STR L"SoftwareTriggersEnabled" #define POISON_ERR_INJ_CTR_STR L"PoisonErrorInjectionsCounter" #define POISON_ERR_CLR_CTR_STR L"PoisonErrorClearCounter" #define MEDIA_TEMP_INJ_CTR_STR L"MediaTemperatureInjectionsCounter" #define SW_TRIGGER_CTR_STR L"SoftwareTriggersCounter" #define SW_TRIGGER_ENABLED_DETAILS_STR L"SoftwareTriggersEnabledDetails" #define MAX_AVG_POWER_LIMIT_STR L"MaxAveragePowerLimit" #define MAX_MEDIA_TEMPERATURE_STR L"MaxMediaTemperature" #define MAX_CONTROLLER_TEMPERATURE_STR L"MaxControllerTemperature" #define MAX_MEMORY_BANDWIDTH_BOOST_MAX_POWER_LIMIT L"MaxMemoryBandwidthBoostMaxPowerLimit" #define MAX_MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT L"MaxMemoryBandwidthBoostAveragePowerTimeConstant" #define MEMORY_BANDWIDTH_BOOST_AVERAGE_POWER_TIME_CONSTANT_STEP L"MemoryBandwidthBoostAveragePowerTimeConstantStep" #define MAX_AVERAGE_POWER_REPORTING_TIME_CONSTANT L"MaxAveragePowerReportingTimeConstant" #define AVERAGE_POWER_REPORTING_TIME_CONSTANT_STEP L"AveragePowerReportingTimeConstantStep" #define MIXED_SKU_STR L"MixedSKU" #define DCPMM_AVERAGE_POWER_STR L"AveragePower" #define AVERAGE_12V_POWER_STR L"Average12vPower" #define AVERAGE_1_2V_POWER_STR L"Average1_2vPower" #define EXTENDED_ADR_ENABLED_STR L"ExtendedAdrEnabled" #define PPC_EXTENDED_ADR_ENABLED_STR L"PpcExtendedAdrEnabled" #define LATCH_SYSTEM_SHUTDOWN_STATE_STR L"LatchSystemShutdownState" #define PREV_PWR_CYCLE_LATCH_SYSTEM_SHUTDOWN_STATE_STR L"PreviousPowerCycleLatchSystemShutdownState" #define MILI_WATT_STR L"mW" #define MEGA_TRANSFERS_PER_SEC_STR L"MT/s" #define BYTE_STR L"b" #define FIPS_MODE_STATUS_STR L"FIPSModeStatus" /* * Register the show dimms command */ EFI_STATUS RegisterShowDimmsCommand(); /* * Execute the show dimms command */ EFI_STATUS ShowDimms(struct Command *pCmd); #endif /* _SHOW_DIMMS_COMMAND_H_ */ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowErrorCommand.c000066400000000000000000001035601440615110200213110ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include "ShowErrorCommand.h" #include "DumpDebugCommand.h" #include "NvmDimmCli.h" #include "NvmInterface.h" #include "LoadCommand.h" #include "Debug.h" #include "Convert.h" /** show -error syntax definition **/ struct Command ShowErrorCommandSyntax = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {ALL_OPTION_SHORT, ALL_OPTION, L"", L"", HELP_ALL_DETAILS_TEXT,FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", LARGE_PAYLOAD_OPTION, L"", L"", HELP_LPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", SMALL_PAYLOAD_OPTION, L"", L"", HELP_SPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT,FALSE, ValueRequired } #else {L"", L"", L"", L"", L"",FALSE, ValueOptional} #endif }, { {ERROR_TARGET, L"", HELP_TEXT_ERROR_LOG, TRUE, ValueRequired}, {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional} }, { {SEQUENCE_NUM_PROPERTY, L"", HELP_TEXT_ERROR_LOG_SEQ_NUM_PROPERTY, FALSE, ValueRequired}, {LEVEL_PROPERTY, L"", HELP_TEXT_ERROR_LOG_LEVEL_PROPERTY, FALSE, ValueRequired}, {COUNT_PROPERTY, L"", HELP_TEXT_ERROR_LOG_COUNT_PROPERTY, FALSE, ValueRequired} }, //!< properties L"Show error log for one or more " PMEM_MODULES_STR L".", //!< help ShowErrorCommand, //!< run function TRUE }; #define DS_ROOT_PATH L"/ErrorList" #define DS_DIMM_PATH L"/ErrorList/Dimm" #define DS_DIMM_INDEX_PATH L"/ErrorList/Dimm[%d]" #define DS_ERROR_PATH L"/ErrorList/Dimm/Error" #define DS_ERROR_INDEX_PATH L"/ErrorList/Dimm[%d]/Error[%d]" /** List heading names **/ #define ERROR_STR L"Error" CHAR16 *mppAllowedShowErrorDisplayValues[] = { DIMM_ID_STR, ERROR_STR }; /* * SHOW MEDIA ERROR ATTRIBUTES (3 columns) * DimmID | System Timestamp | Error Type * ========================================== * 0x0001 | 01/01/1998 00:03:30 | x * ... */ PRINTER_TABLE_ATTRIB ShowMediaErrorTableAttributes = { { { DIMM_ID_STR, //COLUMN HEADER DIMM_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_PATH PATH_KEY_DELIM DIMM_ID_STR //COLUMN DATA PATH }, { ERROR_SYSTEM_TIMESTAMP_STR, //COLUMN HEADER SENSOR_VALUE_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_ERROR_PATH PATH_KEY_DELIM ERROR_SYSTEM_TIMESTAMP_STR //COLUMN DATA PATH }, { ERROR_MEDIA_ERROR_TYPE_STR, //COLUMN HEADER ERROR_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_ERROR_PATH PATH_KEY_DELIM ERROR_MEDIA_ERROR_TYPE_STR //COLUMN DATA PATH }, } }; /* * SHOW THERMAL ERROR ATTRIBUTES (4 columns) * DimmID | System Timestamp | Temperature | Reported * ======================================================= * 0x0001 | x | x | x * ... */ PRINTER_TABLE_ATTRIB ShowThermalErrorTableAttributes = { { { DIMM_ID_STR, //COLUMN HEADER DIMM_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_PATH PATH_KEY_DELIM DIMM_ID_STR //COLUMN DATA PATH }, { ERROR_SYSTEM_TIMESTAMP_STR, //COLUMN HEADER SENSOR_VALUE_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_ERROR_PATH PATH_KEY_DELIM ERROR_SYSTEM_TIMESTAMP_STR //COLUMN DATA PATH }, { ERROR_THERMAL_TEMPERATURE_STR, //COLUMN HEADER TABLE_MIN_HEADER_LENGTH(ERROR_THERMAL_TEMPERATURE_STR), //COLUMN MAX STR WIDTH DS_ERROR_PATH PATH_KEY_DELIM ERROR_THERMAL_TEMPERATURE_STR //COLUMN DATA PATH }, { ERROR_THERMAL_REPORTED_STR, //COLUMN HEADER SENSOR_STATE_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_ERROR_PATH PATH_KEY_DELIM ERROR_THERMAL_REPORTED_STR //COLUMN DATA PATH }, } }; // List view for Media error PRINTER_LIST_ATTRIB ShowMediaErrorListAttributes = { { { DIMM_NODE_STR, //GROUP LEVEL TYPE L"---" DIMM_ID_STR L"=$(" DIMM_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT FORMAT_STR L"=" FORMAT_STR, //NULL or KEY VAL FORMAT STR DIMM_ID_STR //NULL or IGNORE KEY LIST (K1;K2) }, { ERROR_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT L"---" ERROR_STR L"=$(" ERROR_STR L")", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR L"=" FORMAT_STR, //NULL or KEY VAL FORMAT STR ERROR_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; // List view for Thermal error PRINTER_LIST_ATTRIB ShowThermalErrorListAttributes = { { { DIMM_NODE_STR, //GROUP LEVEL TYPE L"---" DIMM_ID_STR L"=$(" DIMM_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT FORMAT_STR L"=" FORMAT_STR, //NULL or KEY VAL FORMAT STR DIMM_ID_STR //NULL or IGNORE KEY LIST (K1;K2) }, { ERROR_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT L"---" ERROR_STR L"=$(" ERROR_STR L")", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR L"=" FORMAT_STR, //NULL or KEY VAL FORMAT STR ERROR_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; PRINTER_DATA_SET_ATTRIBS ShowMediaErrorDataSetAttribs = { &ShowMediaErrorListAttributes, &ShowMediaErrorTableAttributes }; PRINTER_DATA_SET_ATTRIBS ShowThermalErrorDataSetAttribs = { &ShowThermalErrorListAttributes, &ShowThermalErrorTableAttributes }; /** Register the show -error command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowErrorCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowErrorCommandSyntax); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get error log command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS ShowErrorCommand( IN struct Command *pCmd ) { EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; COMMAND_STATUS *pCommandStatus = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; BOOLEAN ThermalError = FALSE; BOOLEAN HighLevel = FALSE; BOOLEAN IsNumber = FALSE; CHAR16 *pTargetValue = NULL; CHAR16 *pPropertyValue = NULL; CHAR16 *pErrorType = NULL; UINT16 *pDimmIds = NULL; UINT32 DimmHandle = 0; UINT32 DimmIndex = 0; UINT16 Index = 0; UINT16 Index2 = 0; UINT32 DimmIdsNum = 0; UINT16 SequenceNum = ERROR_LOG_DEFAULT_SEQUENCE_NUMBER; // By default start from first log UINT32 RequestedCount = ERROR_LOG_MAX_COUNT; // By default get all logs UINT32 ReturnedCount = 0; UINT64 ParsedNumber = 0; ERROR_LOG_INFO ErrorsArray[ERROR_LOG_MAX_COUNT]; MEDIA_ERROR_LOG_INFO *pMediaErrorInfo = NULL; THERMAL_ERROR_LOG_INFO *pThermalErrorInfo = NULL; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; UINT16 ManageableListIndex = 0; UINT64 RangeInBytes = 0; CHAR16 *pTempStr = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; CMD_DISPLAY_OPTIONS *pDispOptions = NULL; BOOLEAN FoundMediaErrorFlag = FALSE; UINT8 MediaErrorInfoCount = 0; BOOLEAN ExcludeInvalidFields = FALSE; NVDIMM_ENTRY(); ZeroMem(ErrorsArray, sizeof(ErrorsArray)); ZeroMem(DimmStr, sizeof(DimmStr)); ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode) || pCommandStatus == NULL) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_NO_COMMAND); goto Finish; } /** Printing will still work via compatibility mode if NULL so no need to check for NULL. **/ pPrinterCtx = pCmd->pPrintCtx; pDispOptions = AllocateZeroPool(sizeof(CMD_DISPLAY_OPTIONS)); if (NULL == pDispOptions) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = CheckAllAndDisplayOptions(pCmd, mppAllowedShowErrorDisplayValues, ALLOWED_DISP_VALUES_COUNT(mppAllowedShowErrorDisplayValues), pDispOptions); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CheckAllAndDisplayOptions has returned error. Code " FORMAT_EFI_STATUS "\n", ReturnCode); goto Finish; } /** Get value of "error" target **/ pTargetValue = GetTargetValue(pCmd, ERROR_TARGET); if (pTargetValue == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_INCOMPLETE_SYNTAX); goto Finish; } if (StrICmp(pTargetValue, ERROR_TARGET_THERMAL_VALUE) == 0) { ThermalError = TRUE; } else if (StrICmp(pTargetValue, ERROR_TARGET_MEDIA_VALUE) == 0) { ThermalError = FALSE; } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_INCORRECT_VALUE_TARGET_ERROR); goto Finish; } /** Open Config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } /** if a specific DIMM pid was passed in, set it **/ if (ContainTarget(pCmd, DIMM_TARGET)) { pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsNum); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Target value is not a valid Dimm ID"); goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsNum)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } } ReturnCode = GetPropertyValue(pCmd, SEQUENCE_NUM_PROPERTY, &pPropertyValue); if (!EFI_ERROR(ReturnCode)) { // If sequence number property exists, check it validity IsNumber = GetU64FromString(pPropertyValue, &ParsedNumber); if (!IsNumber) { NVDIMM_WARN("Sequence number value is not a number"); ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_INCORRECT_VALUE_PROPERTY_SEQ_NUM); goto Finish; } else if (ParsedNumber > ERROR_LOG_MAX_SEQUENCE_NUMBER) { NVDIMM_WARN("Sequence number value %d is greater than maximum %d", ParsedNumber, ERROR_LOG_MAX_SEQUENCE_NUMBER); ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_INCORRECT_VALUE_PROPERTY_SEQ_NUM); goto Finish; } SequenceNum = (UINT16)ParsedNumber; } else { // If sequence number property doesn't exists is ok, it is optional param, using default value ReturnCode = EFI_SUCCESS; } ReturnCode = GetPropertyValue(pCmd, LEVEL_PROPERTY, &pPropertyValue); if (!EFI_ERROR(ReturnCode)) { // If level property exists, check it validity if (StrICmp(pPropertyValue, LEVEL_HIGH_PROPERTY_VALUE) == 0) { HighLevel = TRUE; } else if (StrICmp(pPropertyValue, LEVEL_LOW_PROPERTY_VALUE) == 0) { HighLevel = FALSE; } else { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_WARN("Invalid Error Level. Error Level can be %s or %s", LEVEL_HIGH_PROPERTY_VALUE, LEVEL_LOW_PROPERTY_VALUE); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_INCORRECT_VALUE_PROPERTY_LEVEL); goto Finish; } } else { // If level property doesn't exists is ok, it is optional param, using default value HighLevel = TRUE; ReturnCode = EFI_SUCCESS; } ReturnCode = GetPropertyValue(pCmd, COUNT_PROPERTY, &pPropertyValue); if (!EFI_ERROR(ReturnCode)) { // If Count property exists, check it validity IsNumber = GetU64FromString(pPropertyValue, &ParsedNumber); if (!IsNumber) { NVDIMM_WARN("Count value is not a number"); ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_INCORRECT_VALUE_PROPERTY_COUNT); goto Finish; } else if (ParsedNumber > ERROR_LOG_MAX_COUNT) { NVDIMM_WARN("Count value %d is greater than maximum %d", ParsedNumber, ERROR_LOG_MAX_COUNT); ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_INCORRECT_VALUE_PROPERTY_COUNT); goto Finish; } RequestedCount = (UINT32)ParsedNumber; } else { // If count property doesn't exists is ok, it is optional param, using default value ReturnCode = EFI_SUCCESS; } if (DimmIdsNum == 0) { DimmIdsNum = DimmCount; FREE_POOL_SAFE(pDimmIds); pDimmIds = AllocateZeroPool(sizeof(*pDimmIds) * DimmIdsNum); if (pDimmIds == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); NVDIMM_WARN("Failed on memory allocation."); goto Finish; } for (Index = 0; Index < DimmIdsNum; Index++) { if (MANAGEMENT_VALID_CONFIG == pDimms[Index].ManageabilityState){ pDimmIds[ManageableListIndex] = pDimms[Index].DimmID; ManageableListIndex++; } } DimmIdsNum = ManageableListIndex; } if (DimmIdsNum == 0) { ResetCmdStatus(pCommandStatus, NVM_ERR_MANAGEABLE_DIMM_NOT_FOUND); goto Finish; } for (Index = 0; Index < DimmIdsNum; Index++) { ReturnedCount = RequestedCount; ReturnCode = GetDimmHandleByPid(pDimmIds[Index], pDimms, DimmCount, &DimmHandle, &DimmIndex); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } ReturnCode = GetPreferredDimmIdAsString(DimmHandle, pDimms[DimmIndex].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetErrorLog(pNvmDimmConfigProtocol, &pDimmIds[Index], 1, ThermalError, SequenceNum, HighLevel, &ReturnedCount, ErrorsArray, pCommandStatus); if (EFI_ERROR(ReturnCode)) { if (pCommandStatus->GeneralStatus != NVM_SUCCESS) { ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); } PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to get error logs from " PMEM_MODULE_STR L" " FORMAT_STR L"\n", DimmStr); continue; } if (ReturnedCount == 0) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"No errors found on " PMEM_MODULE_STR L" " FORMAT_STR L"\n", DimmStr); } else { PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_INDEX_PATH, Index); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, DimmStr); for (Index2 = 0; Index2 < ReturnedCount; Index2++) { pErrorType = (ErrorsArray[Index2].ErrorType == THERMAL_ERROR ? ERROR_THERMAL_OCCURRED_STR : ERROR_MEDIA_OCCURRED_STR); PRINTER_BUILD_KEY_PATH(pPath, DS_ERROR_INDEX_PATH, Index, Index2); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_STR, pErrorType); pTempStr = GetTimeFormatString(ErrorsArray[Index2].SystemTimestamp, FALSE); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_SYSTEM_TIMESTAMP_STR, pTempStr); FREE_POOL_SAFE(pTempStr); if (ErrorsArray[Index2].ErrorType == THERMAL_ERROR) { pThermalErrorInfo = (THERMAL_ERROR_LOG_INFO *)ErrorsArray[Index2].OutputData; PRINTER_SET_KEY_VAL_UINT16(pPrinterCtx, pPath, ERROR_THERMAL_TEMPERATURE_STR, pThermalErrorInfo->Temperature, DECIMAL); // Thermal Reported if (pThermalErrorInfo->Reported == ERROR_THERMAL_REPORTED_USER_ALARM) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_THERMAL_REPORTED_STR, ERROR_THERMAL_REPORTED_USER_ALARM_STR); } else if (pThermalErrorInfo->Reported == ERROR_THERMAL_REPORTED_LOW) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_THERMAL_REPORTED_STR, ERROR_THERMAL_REPORTED_LOW_STR); } else if (pThermalErrorInfo->Reported == ERROR_THERMAL_REPORTED_HIGH) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_THERMAL_REPORTED_STR, ERROR_THERMAL_REPORTED_HIGH_STR); } else if (pThermalErrorInfo->Reported == ERROR_THERMAL_REPORTED_CRITICAL) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_THERMAL_REPORTED_STR, ERROR_THERMAL_REPORTED_CRITICAL_STR); } else { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_THERMAL_REPORTED_STR, ERROR_THERMAL_REPORTED_UNKNOWN_STR); } // Temperature Type if (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ERROR_SEQUENCE_NUMBER))) { if (pThermalErrorInfo->Type == ERROR_THERMAL_TYPE_MEDIA) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_THERMAL_TYPE_STR, ERROR_THERMAL_TYPE_MEDIA_STR); } else if (pThermalErrorInfo->Type == ERROR_THERMAL_TYPE_CONTROLLER) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_THERMAL_TYPE_STR, ERROR_THERMAL_TYPE_CONTROLLER_STR); } else { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_THERMAL_TYPE_STR, ERROR_THERMAL_TYPE_UNKNOWN_STR); } } if (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ERROR_SEQUENCE_NUMBER))) { PRINTER_SET_KEY_VAL_UINT16(pPrinterCtx, pPath, ERROR_SEQUENCE_NUMBER, pThermalErrorInfo->SequenceNum, DECIMAL); } } else { pMediaErrorInfo = (MEDIA_ERROR_LOG_INFO *)ErrorsArray[Index2].OutputData; ExcludeInvalidFields = FALSE; // Error Type switch (pMediaErrorInfo->ErrorType) { case ERROR_TYPE_UNCORRECTABLE: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_UNCORRECTABLE, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_UNCORRECTABLE_STR); break; case ERROR_TYPE_DPA_MISMATCH: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_DPA_MISMATCH, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_DPA_MISMATCH_STR); break; case ERROR_TYPE_AIT_ERROR: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_AIT_ERROR, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_AIT_ERROR_STR); break; case ERROR_TYPE_DATA_PATH_ERROR: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_DATA_PATH_ERROR, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_DATA_PATH_ERROR_STR); break; case ERROR_TYPE_LOCKED_ILLEGAL_ACCESS: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_LOCKED_ILLEGAL_ACCESS, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_LOCKED_ILLEGAL_ACCESS_STR); break; case ERROR_TYPE_PERCENTAGE_REMAINING: ExcludeInvalidFields = TRUE; PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_PERCENTAGE_REMAINING, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_PERCENTAGE_REMAINING_STR); break; case ERROR_TYPE_SMART_CHANGE: ExcludeInvalidFields = TRUE; PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_SMART_CHANGE, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_SMART_CHANGE_STR); break; case ERROR_TYPE_PERSISTENT_WRITE_ECC: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_PERSISTENT_WRITE_ECC, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_PERSISTENT_WRITE_ECC_STR); break; default: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_TYPE_STR, ERROR_TYPE_UNKNOWN_STR); break; } // Transaction Type if (!ExcludeInvalidFields && (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ERROR_MEDIA_TRANSACTION_TYPE_STR)))) { switch (pMediaErrorInfo->TransactionType) { case TRANSACTION_TYPE_2LM_READ: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_2LM_READ, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_2LM_READ_STR); break; case TRANSACTION_TYPE_2LM_WRITE: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_2LM_WRITE, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_2LM_WRITE_STR); break; case TRANSACTION_TYPE_PM_READ: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_PM_READ, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_PM_READ_STR); break; case TRANSACTION_TYPE_PM_WRITE: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_PM_WRITE, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_PM_WRITE_STR); break; case TRANSACTION_TYPE_AIT_READ: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_AIT_READ, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_AIT_READ_STR); break; case TRANSACTION_TYPE_AIT_WRITE: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_AIT_WRITE, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_AIT_WRITE_STR); break; case TRANSACTION_TYPE_WEAR_LEVEL_MOVE: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_WEAR_LEVEL_MOVE, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_WEAR_LEVEL_MOVE_STR); break; case TRANSACTION_TYPE_PATROL_SCRUB: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_PATROL_SCRUB, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_PATROL_SCRUB_STR); break; case TRANSACTION_TYPE_CSR_READ: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_CSR_READ, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_CSR_READ_STR); break; case TRANSACTION_TYPE_CSR_WRITE: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_CSR_WRITE, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_CSR_WRITE_STR); break; case TRANSACTION_TYPE_ARS: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_ARS, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_ARS_STR); break; case TRANSACTION_TYPE_UNAVAILABLE: PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_UNAVAILABLE, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, ERROR_MSG_EXTRA_SPACE); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_UNAVAILABLE_STR); break; default: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, TRANSACTION_TYPE_UNKNOWN_STR); break; } } else if (ExcludeInvalidFields) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_TRANSACTION_TYPE_STR, NA_STR); } // Media Error info flags if (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ERROR_MEDIA_ERROR_FLAGS_STR))) { if (pMediaErrorInfo->PdaValid) { MediaErrorInfoCount = MediaErrorInfoCount | pMediaErrorInfo->PdaValid; } if (pMediaErrorInfo->DpaValid) { MediaErrorInfoCount = MediaErrorInfoCount | (pMediaErrorInfo->DpaValid << 1); } if (pMediaErrorInfo->Interrupt) { MediaErrorInfoCount = MediaErrorInfoCount | (pMediaErrorInfo->Interrupt << 2); } PRINTER_SET_KEY_VAL_UINT8(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_FLAGS_STR, MediaErrorInfoCount, HEX); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_FLAGS_STR, ERROR_MSG_EXTRA_SPACE); if (pMediaErrorInfo->PdaValid) { PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_FLAGS_STR, ERROR_FLAGS_PDA_VALID_STR); FoundMediaErrorFlag = TRUE; } if (pMediaErrorInfo->DpaValid) { if (FoundMediaErrorFlag == TRUE) { PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_FLAGS_STR, ERROR_MSG_COMA_CHAR); } PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_FLAGS_STR, ERROR_FLAGS_DPA_VALID_STR); FoundMediaErrorFlag = TRUE; } if (pMediaErrorInfo->Interrupt) { if (FoundMediaErrorFlag == TRUE) { PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_FLAGS_STR, ERROR_MSG_COMA_CHAR); } PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_ERROR_FLAGS_STR, ERROR_FLAGS_INTERRUPT_STR); } FoundMediaErrorFlag = FALSE; MediaErrorInfoCount = 0; } if (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ERROR_MEDIA_DPA_STR))) { if (ExcludeInvalidFields || !pMediaErrorInfo->DpaValid) { PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_DPA_STR, NA_STR); } else { PRINTER_SET_KEY_VAL_UINT64(pPrinterCtx, pPath, ERROR_MEDIA_DPA_STR, pMediaErrorInfo->Dpa, HEX); } } if (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ERROR_MEDIA_PDA_STR))) { if (ExcludeInvalidFields || !pMediaErrorInfo->PdaValid) { PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_PDA_STR, NA_STR); } else { PRINTER_SET_KEY_VAL_UINT64(pPrinterCtx, pPath, ERROR_MEDIA_PDA_STR, pMediaErrorInfo->Pda, HEX); } } // Range in bytes if (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ERROR_MEDIA_RANGE_STR))) { RangeInBytes = Pow(2, pMediaErrorInfo->Range); if (ExcludeInvalidFields) { PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_RANGE_STR, NA_STR); } else { PRINTER_SET_KEY_VAL_UINT64(pPrinterCtx, pPath, ERROR_MEDIA_RANGE_STR, RangeInBytes, DECIMAL); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ERROR_MEDIA_RANGE_STR, ERROR_MSG_BYTE_CHAR); } } if (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ERROR_SEQUENCE_NUMBER))) { PRINTER_SET_KEY_VAL_UINT64(pPrinterCtx, pPath, ERROR_SEQUENCE_NUMBER, pMediaErrorInfo->SequenceNum, DECIMAL); } } } } } if (ThermalError == FALSE) { PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowMediaErrorDataSetAttribs); } else { PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowThermalErrorDataSetAttribs); } Finish: PRINTER_SET_COMMAND_STATUS(pCmd->pPrintCtx, ReturnCode, L"Show Error", CLI_INFO_ON, pCommandStatus); PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pPath); FreeCommandStatus(&pCommandStatus); FREE_CMD_DISPLAY_OPTIONS_SAFE(pDispOptions); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowErrorCommand.h000066400000000000000000000116001440615110200213070ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_ERROR_COMMAND_H_ #define _SHOW_ERROR_COMMAND_H_ #include #include "NvmInterface.h" #include "Common.h" #define ERROR_MAX_STR_WIDTH 36 #define ERROR_MIN_STR_WIDTH 8 #define ERROR_MSG_EXTRA_SPACE L" - " #define ERROR_MSG_BYTE_CHAR L"B" #define ERROR_MSG_COMA_CHAR L", " #define ERROR_THERMAL_OCCURRED_STR L"Thermal" #define ERROR_MEDIA_OCCURRED_STR L"Media" #define ERROR_SYSTEM_TIMESTAMP_STR L"System Timestamp" #define ERROR_THERMAL_TEMPERATURE_STR L"Temperature" #define ERROR_THERMAL_REPORTED_STR L"Reported" #define ERROR_THERMAL_TYPE_STR L"Temperature Type" #define ERROR_MEDIA_DPA_STR L"DPA" #define ERROR_MEDIA_PDA_STR L"PDA" #define ERROR_MEDIA_RANGE_STR L"Range" #define ERROR_MEDIA_ERROR_TYPE_STR L"Error Type" #define ERROR_MEDIA_ERROR_FLAGS_STR L"Error Flags" #define ERROR_MEDIA_TRANSACTION_TYPE_STR L"Transaction Type" #define ERROR_SEQUENCE_NUMBER L"Sequence Number" #define ERROR_THERMAL_REPORTED_USER_ALARM 0 //0b000 #define ERROR_THERMAL_REPORTED_LOW 1 //0b001 #define ERROR_THERMAL_REPORTED_HIGH 2 //0b010 #define ERROR_THERMAL_REPORTED_CRITICAL 4 //0b100 #define ERROR_THERMAL_REPORTED_USER_ALARM_STR L"User Alarm Trip" #define ERROR_THERMAL_REPORTED_LOW_STR L"Low" #define ERROR_THERMAL_REPORTED_HIGH_STR L"High" #define ERROR_THERMAL_REPORTED_CRITICAL_STR L"Critical" #define ERROR_THERMAL_REPORTED_UNKNOWN_STR L"Unknown" #define ERROR_THERMAL_TYPE_MEDIA 0x00 #define ERROR_THERMAL_TYPE_CONTROLLER 0x01 #define ERROR_THERMAL_TYPE_MEDIA_STR L"Media Temperature" #define ERROR_THERMAL_TYPE_CONTROLLER_STR L"Controller Temperature" #define ERROR_THERMAL_TYPE_UNKNOWN_STR L"Unknown" #define ERROR_TYPE_UNCORRECTABLE 0x00 #define ERROR_TYPE_DPA_MISMATCH 0x01 #define ERROR_TYPE_AIT_ERROR 0x02 #define ERROR_TYPE_DATA_PATH_ERROR 0x03 #define ERROR_TYPE_LOCKED_ILLEGAL_ACCESS 0x04 #define ERROR_TYPE_PERCENTAGE_REMAINING 0x05 #define ERROR_TYPE_SMART_CHANGE 0x06 #define ERROR_TYPE_PERSISTENT_WRITE_ECC 0x07 #define ERROR_TYPE_UNCORRECTABLE_STR L"Uncorrectable" #define ERROR_TYPE_DPA_MISMATCH_STR L"DPA Mismatch" #define ERROR_TYPE_AIT_ERROR_STR L"AIT Error" #define ERROR_TYPE_DATA_PATH_ERROR_STR L"Data Path Error" #define ERROR_TYPE_LOCKED_ILLEGAL_ACCESS_STR L"Locked/Illegal Access" #define ERROR_TYPE_PERCENTAGE_REMAINING_STR L"User Percentage Remaining Alarm Trip" #define ERROR_TYPE_SMART_CHANGE_STR L"Smart Health Status Change" #define ERROR_TYPE_PERSISTENT_WRITE_ECC_STR L"Persistent Write ECC" #define ERROR_TYPE_UNKNOWN_STR L"Unknown" #define ERROR_FLAGS_PDA_VALID_STR L"PDA Valid" #define ERROR_FLAGS_DPA_VALID_STR L"DPA Valid" #define ERROR_FLAGS_INTERRUPT_STR L"Interrupt" #define ERROR_FLAGS_VIRAL_STR L"Viral" #define TRANSACTION_TYPE_2LM_READ 0x00 #define TRANSACTION_TYPE_2LM_WRITE 0x01 #define TRANSACTION_TYPE_PM_READ 0x02 #define TRANSACTION_TYPE_PM_WRITE 0x03 #define TRANSACTION_TYPE_AIT_READ 0x06 #define TRANSACTION_TYPE_AIT_WRITE 0x07 #define TRANSACTION_TYPE_WEAR_LEVEL_MOVE 0x08 #define TRANSACTION_TYPE_PATROL_SCRUB 0x09 #define TRANSACTION_TYPE_CSR_READ 0x0A #define TRANSACTION_TYPE_CSR_WRITE 0x0B #define TRANSACTION_TYPE_ARS 0x0C #define TRANSACTION_TYPE_UNAVAILABLE 0x0D #define TRANSACTION_TYPE_2LM_READ_STR L"2LM Read" #define TRANSACTION_TYPE_2LM_WRITE_STR L"2LM Write" #define TRANSACTION_TYPE_PM_READ_STR L"PM Read" #define TRANSACTION_TYPE_PM_WRITE_STR L"PM Write" #define TRANSACTION_TYPE_AIT_READ_STR L"AIT Read" #define TRANSACTION_TYPE_AIT_WRITE_STR L"AIT Write" #define TRANSACTION_TYPE_WEAR_LEVEL_MOVE_STR L"Wear Level Mode" #define TRANSACTION_TYPE_PATROL_SCRUB_STR L"Patrol Scrub" #define TRANSACTION_TYPE_CSR_READ_STR L"CSR Read" #define TRANSACTION_TYPE_CSR_WRITE_STR L"CSR Write" #define TRANSACTION_TYPE_ARS_STR L"Address Range Scrub" #define TRANSACTION_TYPE_UNAVAILABLE_STR L"Unavailable" #define TRANSACTION_TYPE_UNKNOWN_STR L"Unknown" #define HELP_TEXT_ERROR_LOG_SEQ_NUM_PROPERTY L"<1, 65535>" #define HELP_TEXT_ERROR_LOG_COUNT_PROPERTY L"<0, 255>" #define HELP_TEXT_ERROR_LOG_LEVEL_PROPERTY L"Low|High" /** Register syntax of show -error **/ EFI_STATUS RegisterShowErrorCommand( ); /** Get error log command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS ShowErrorCommand( IN struct Command *pCmd ); #endif ipmctl-03.00.00.0485/DcpmPkg/cli/ShowFirmwareCommand.c000066400000000000000000000304151440615110200217720ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "ShowFirmwareCommand.h" #include "NvmDimmCli.h" #define ACTIVE_FW_VERSION_STR L"ActiveFWVersion" #define STAGED_FW_VERSION_STR L"StagedFWVersion" #define STAGED_FW_ACTIVATABLE_STR L"StagedFWActivatable" #define FW_UPDATE_STATUS_STR L"FWUpdateStatus" #define FW_IMAGE_MAX_SIZE_STR L"FWImageMaxSize" #define QUIESCE_REQUIRED_STR L"QuiesceRequired" #define ACTIVATION_TIME_STR L"ActivationTime" #define DS_ROOT_PATH L"/DimmFirmwareList" #define DS_DIMM_FW_PATH L"/DimmFirmwareList/DimmFirmware" #define DS_DIMM_FW_INDEX_PATH L"/DimmFirmwareList/DimmFirmware[%d]" #define DS_GROUP_STR L"DimmFirmware" /* * PRINT LIST ATTRIBUTES * ---DimmId=0x0001--- * ActiveFwVersion=X * StagedFwVersion=X * ... */ PRINTER_LIST_ATTRIB ShowFirmwareListAttributes = { { { DS_GROUP_STR, //GROUP LEVEL TYPE L"---" DIMM_ID_STR L"=$(" DIMM_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR DIMM_ID_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; /* * PRINTER TABLE ATTRIBUTES (3 columns) * DimmID | ActiveFwVersion | StagedFwVersion * ================================================ * 0x0001 | aa.bb.cc.dddd | aa.bb.cc.dddd * ... */ PRINTER_TABLE_ATTRIB ShowFirmwareTableAttributes = { { { DIMM_ID_STR, //COLUMN HEADER DIMM_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_FW_PATH PATH_KEY_DELIM DIMM_ID_STR //COLUMN DATA PATH }, { ACTIVE_FW_VERSION_STR, //COLUMN HEADER ACTIVE_FW_VERSION_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_FW_PATH PATH_KEY_DELIM ACTIVE_FW_VERSION_STR //COLUMN DATA PATH }, { STAGED_FW_VERSION_STR, //COLUMN HEADER STAGED_FW_VERSION_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_FW_PATH PATH_KEY_DELIM STAGED_FW_VERSION_STR //COLUMN DATA PATH } } }; PRINTER_DATA_SET_ATTRIBS ShowFirmwareDataSetAttribs = { &ShowFirmwareListAttributes, &ShowFirmwareTableAttributes }; /** Command syntax definition **/ struct Command ShowFirmwareCommand = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {ALL_OPTION_SHORT, ALL_OPTION, L"", L"",HELP_ALL_DETAILS_TEXT, FALSE, ValueEmpty}, {DISPLAY_OPTION_SHORT, DISPLAY_OPTION, L"", HELP_TEXT_ATTRIBUTES,HELP_DISPLAY_DETAILS_TEXT, FALSE, ValueRequired} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, { //!< targets {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional}, {FIRMWARE_TARGET, L"", L"", TRUE, ValueEmpty} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show information about firmware on one or more " PMEM_MODULES_STR L".", //!< help ShowFirmware, //!< run function TRUE, //!< enable print control support }; CHAR16 *mppAllowedShowFirmwareDisplayValues[] = { DIMM_ID_STR, ACTIVE_FW_VERSION_STR, STAGED_FW_VERSION_STR, STAGED_FW_ACTIVATABLE_STR, FW_UPDATE_STATUS_STR, FW_IMAGE_MAX_SIZE_STR, QUIESCE_REQUIRED_STR, ACTIVATION_TIME_STR }; /** Register the show firmware command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowFirmwareCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowFirmwareCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Execute the show firmware command @param[in] pCmd command from CLI @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS ShowFirmware( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; COMMAND_STATUS *pCommandStatus = NULL; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; CHAR16 *pTargetValue = NULL; UINT16 *pDimmIds = NULL; UINT32 DimmIdsCount = 0; UINT32 Index = 0; CHAR16 *pFwUpdateStatusString = NULL; CHAR16 *pQuiesceRequiredString = NULL; CHAR16 *pStagedFwActivatableString = NULL; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; CMD_DISPLAY_OPTIONS *pDispOptions = NULL; BOOLEAN ShowAll = FALSE; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; CHAR16 FwVerStr[FW_VERSION_LEN]; UINT8 ManageableDimmsCount = 0; NVDIMM_ENTRY(); ZeroMem(DimmStr, sizeof(DimmStr)); if (pCmd == NULL || pCmd->pPrintCtx == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; pDispOptions = AllocateZeroPool(sizeof(CMD_DISPLAY_OPTIONS)); if (NULL == pDispOptions) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = CheckAllAndDisplayOptions(pCmd, mppAllowedShowFirmwareDisplayValues, ALLOWED_DISP_VALUES_COUNT(mppAllowedShowFirmwareDisplayValues), pDispOptions); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CheckAllAndDisplayOptions has returned error. Code " FORMAT_EFI_STATUS "\n", ReturnCode); goto Finish; } ShowAll = (!pDispOptions->AllOptionSet && !pDispOptions->DisplayOptionSet) || pDispOptions->AllOptionSet; /** Make sure we can access the config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information CHECK_RESULT(GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_FW_IMAGE_INFO, &pDimms, &DimmCount), Finish); /** Initialize status structure **/ ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_ABORTED; NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } /** Get DCPMMs list **/ if (ContainTarget(pCmd, DIMM_TARGET)) { pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsCount); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsCount)){ ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } } /** Print table **/ for (Index = 0; Index < DimmCount; Index++) { if (DimmIdsCount > 0) { if (!ContainUint(pDimmIds, DimmIdsCount, pDimms[Index].DimmID)) { continue; } } if (pDimms[Index].ManageabilityState != MANAGEMENT_VALID_CONFIG) { continue; } ManageableDimmsCount++; ReturnCode = GetPreferredDimmIdAsString(pDimms[Index].DimmHandle, pDimms[Index].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_FW_INDEX_PATH, Index); /** DimmID **/ PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, DimmStr); /** ActiveFwVersion **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ACTIVE_FW_VERSION_STR))) { ConvertFwVersion(FwVerStr, pDimms[Index].FwVer.FwProduct, pDimms[Index].FwVer.FwRevision, pDimms[Index].FwVer.FwSecurityVersion, pDimms[Index].FwVer.FwBuild); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ACTIVE_FW_VERSION_STR, FwVerStr); } /** StagedFwVersion **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, STAGED_FW_VERSION_STR))) { ConvertFwVersion(FwVerStr, pDimms[Index].StagedFwVersion.FwProduct, pDimms[Index].StagedFwVersion.FwRevision, pDimms[Index].StagedFwVersion.FwSecurityVersion, pDimms[Index].StagedFwVersion.FwBuild); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, STAGED_FW_VERSION_STR, FwVerStr); } /** StagedFwActivatable **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, STAGED_FW_ACTIVATABLE_STR))) { pStagedFwActivatableString = StagedFwActivatableToString(gNvmDimmCliHiiHandle, pDimms[Index].StagedFwActivatable); if (pStagedFwActivatableString == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, STAGED_FW_ACTIVATABLE_STR, pStagedFwActivatableString); FREE_POOL_SAFE(pStagedFwActivatableString); } /** FwUpdateStatus **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, FW_UPDATE_STATUS_STR))) { pFwUpdateStatusString = LastFwUpdateStatusToString(gNvmDimmCliHiiHandle, pDimms[Index].LastFwUpdateStatus); if (pFwUpdateStatusString == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, FW_UPDATE_STATUS_STR, pFwUpdateStatusString); FREE_POOL_SAFE(pFwUpdateStatusString); } /** FwImageMaxSize **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, FW_IMAGE_MAX_SIZE_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, FW_IMAGE_MAX_SIZE_STR, FORMAT_UINT32, pDimms[Index].FWImageMaxSize); } /** Quiesce Required **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, QUIESCE_REQUIRED_STR))) { pQuiesceRequiredString = QuiesceRequiredToString(gNvmDimmCliHiiHandle, pDimms[Index].QuiesceRequired); if (pQuiesceRequiredString == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, QUIESCE_REQUIRED_STR, pQuiesceRequiredString); FREE_POOL_SAFE(pQuiesceRequiredString); } /** Activation Time **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ACTIVATION_TIME_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, ACTIVATION_TIME_STR, FORMAT_UINT32, pDimms[Index].ActivationTime); } } if (ManageableDimmsCount == 0) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_INFO_NO_MANAGEABLE_DIMMS); goto Finish; } ReturnCode = EFI_SUCCESS; //Specify table attributes PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowFirmwareDataSetAttribs); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pPath); FREE_CMD_DISPLAY_OPTIONS_SAFE(pDispOptions); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimms); FreeCommandStatus(&pCommandStatus); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowFirmwareCommand.h000066400000000000000000000017331440615110200220000ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_FIRMWARE_COMMAND_H_ #define _SHOW_FIRMWARE_COMMAND_H_ #include #include #include #include #include #include #include #include "Common.h" #include /** Register the show firmware command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowFirmwareCommand( ); /** Execute the show firmware command @param[in] pCmd command from CLI @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS ShowFirmware( IN struct Command *pCmd ); #endif /* _SHOW_FIRMWARE_COMMAND_H_ */ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowGoalCommand.c000066400000000000000000000513361440615110200211050ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "Debug.h" #include "Types.h" #include #include "Utility.h" #include "NvmDimmCli.h" #include "NvmInterface.h" #include "CommandParser.h" #include "ShowGoalCommand.h" #include "Common.h" #include "Convert.h" #define DS_ROOT_PATH L"/ConfigGoalList" #define DS_CONFIG_GOAL_PATH L"/ConfigGoalList/ConfigGoal" #define DS_CONFIG_GOAL_INDEX_PATH L"/ConfigGoalList/ConfigGoal[%d]" /* * PRINT LIST ATTRIBUTES * ---DimmID=0x0001--- * SocketID=X * MemorySize=X * ... */ PRINTER_LIST_ATTRIB ShowGoalListAttributes = { { { CONFIG_GOAL_NODE_STR, //GROUP LEVEL TYPE L"---" DIMM_ID_STR L"=$(" DIMM_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR DIMM_ID_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; /* * PRINTER TABLE ATTRIBUTES ( columns) * SocketID | DimmID | MemorySize | AppDirect1Size | AppDirect2Size * ================================================================ * 0x0001 | X |X | X | X * ... */ PRINTER_TABLE_ATTRIB ShowGoalTableAttributes = { { { SOCKET_ID_STR, //COLUMN HEADER SOCKET_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_CONFIG_GOAL_PATH PATH_KEY_DELIM SOCKET_ID_STR //COLUMN DATA PATH }, { DIMM_ID_STR, //COLUMN HEADER DIMM_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_CONFIG_GOAL_PATH PATH_KEY_DELIM DIMM_ID_STR //COLUMN DATA PATH }, { MEMORY_SIZE_PROPERTY, //COLUMN HEADER MEMORY_SIZE_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_CONFIG_GOAL_PATH PATH_KEY_DELIM MEMORY_SIZE_PROPERTY //COLUMN DATA PATH }, { APPDIRECT_1_SIZE_PROPERTY, //COLUMN HEADER MEMORY_SIZE_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_CONFIG_GOAL_PATH PATH_KEY_DELIM APPDIRECT_1_SIZE_PROPERTY //COLUMN DATA PATH }, { APPDIRECT_2_SIZE_PROPERTY, //COLUMN HEADER MEMORY_SIZE_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_CONFIG_GOAL_PATH PATH_KEY_DELIM APPDIRECT_2_SIZE_PROPERTY //COLUMN DATA PATH } } }; PRINTER_DATA_SET_ATTRIBS ShowGoalDataSetAttribs = { &ShowGoalListAttributes, &ShowGoalTableAttributes }; /** Command syntax definition **/ struct Command ShowGoalCommand = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {ALL_OPTION_SHORT, ALL_OPTION, L"", L"",HELP_ALL_DETAILS_TEXT, FALSE, ValueEmpty}, {DISPLAY_OPTION_SHORT, DISPLAY_OPTION, L"", HELP_TEXT_ATTRIBUTES,HELP_DISPLAY_DETAILS_TEXT, FALSE, ValueRequired}, {UNITS_OPTION_SHORT, UNITS_OPTION, L"", UNITS_OPTION_HELP,HELP_UNIT_DETAILS_TEXT, FALSE, ValueRequired} #ifdef OS_BUILD , {OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired} #endif // OS_BUILD }, { //!< targets {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional}, {GOAL_TARGET, L"", L"", TRUE, ValueEmpty}, {SOCKET_TARGET, L"", HELP_TEXT_SOCKET_IDS, FALSE, ValueOptional} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show a pending memory allocation goal on one or more " PMEM_MODULES_STR L" to be applied on reboot.", //!< help ShowGoal, TRUE, //!< enable print control support }; CHAR16 *mppAllowedShowGoalDisplayValues[] = { SOCKET_ID_STR, DIMM_ID_STR, MEMORY_SIZE_PROPERTY, APPDIRECT_SIZE_PROPERTY, APPDIRECT_INDEX_PROPERTY, APPDIRECT_1_SIZE_PROPERTY, APPDIRECT_1_SETTINGS_PROPERTY, APPDIRECT_1_INDEX_PROPERTY, APPDIRECT_2_SIZE_PROPERTY, APPDIRECT_2_SETTINGS_PROPERTY, APPDIRECT_2_INDEX_PROPERTY, STATUS_STR }; /** Print results of show goal according to table view @param[in] pCmd command from CLI @param[in] pRegionConfigsInfo - Region Config table to be printed @param[in] CurrentUnits The requested type of units to convert the capacity into @param[in] RegionConfigsCount - Number of elements in array @retval EFI_SUCCESS if printing is successful @retval EFI_INVALID_PARAMETER if input parameter is incorrect **/ EFI_STATUS ShowGoalPrintTableView( IN struct Command *pCmd, IN REGION_GOAL_PER_DIMM_INFO *pRegionConfigsInfo, IN UINT16 CurrentUnits, IN UINT32 RegionConfigsCount, IN BOOLEAN Buffered ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TempReturnCode = EFI_SUCCESS; UINT32 Index = 0; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; CHAR16 *pVolatileCapacityStr = NULL; CHAR16 *pAppDirect1CapacityStr = NULL; CHAR16 *pAppDirect2CapacityStr = NULL; REGION_GOAL_PER_DIMM_INFO *pCurrentGoal = NULL; CHAR16 *pPath = NULL; ZeroMem(DimmStr, sizeof(DimmStr)); if (pRegionConfigsInfo == NULL) { goto Finish; } for (Index = 0; Index < RegionConfigsCount; ++Index) { pCurrentGoal = &pRegionConfigsInfo[Index]; PRINTER_BUILD_KEY_PATH(pPath, DS_CONFIG_GOAL_INDEX_PATH, Index); ReturnCode = GetPreferredDimmIdAsString(pCurrentGoal->DimmID, pCurrentGoal->DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, ROUNDDOWN(pCurrentGoal->VolatileSize, SIZE_1GB), CurrentUnits, TRUE, &pVolatileCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pCurrentGoal->AppDirectSize[0], CurrentUnits, TRUE, &pAppDirect1CapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pCurrentGoal->AppDirectSize[1], CurrentUnits, TRUE, &pAppDirect2CapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pCmd->pPrintCtx, pPath, SOCKET_ID_STR, FORMAT_HEX, pCurrentGoal->SocketId); PRINTER_SET_KEY_VAL_WIDE_STR(pCmd->pPrintCtx, pPath, DIMM_ID_STR, DimmStr); PRINTER_SET_KEY_VAL_WIDE_STR(pCmd->pPrintCtx, pPath, MEMORY_SIZE_PROPERTY, pVolatileCapacityStr); PRINTER_SET_KEY_VAL_WIDE_STR(pCmd->pPrintCtx, pPath, APPDIRECT_1_SIZE_PROPERTY, pAppDirect1CapacityStr); PRINTER_SET_KEY_VAL_WIDE_STR(pCmd->pPrintCtx, pPath, APPDIRECT_2_SIZE_PROPERTY, pAppDirect2CapacityStr); FREE_POOL_SAFE(pVolatileCapacityStr); FREE_POOL_SAFE(pAppDirect1CapacityStr); FREE_POOL_SAFE(pAppDirect2CapacityStr); } Finish: //Specify table attributes PRINTER_CONFIGURE_DATA_ATTRIBUTES(pCmd->pPrintCtx, DS_ROOT_PATH, &ShowGoalDataSetAttribs); FREE_POOL_SAFE(pPath); return ReturnCode; } /** Print results of show goal according to detailed view @param[in] pCmd command from CLI @param[in] pRegionConfigsInfo - Region Config table to be printed @param[in] RegionConfigsCount - Number of elements in array @param[in] AllOptionSet - Print all display options @param[in] DisplayOptionSet - Print specified display options @param[in] CurrentUnits The requested type of units to convert the capacity into @param[in] pDisplayValues - Selected display options @retval EFI_SUCCESS if printing is successful @retval EFI_INVALID_PARAMETER if input parameter is incorrect **/ EFI_STATUS ShowGoalPrintDetailedView( IN struct Command *pCmd, IN REGION_GOAL_PER_DIMM_INFO *pRegionConfigsInfo, IN UINT32 RegionConfigsCount, IN BOOLEAN AllOptionSet, IN BOOLEAN DisplayOptionSet, IN UINT16 CurrentUnits, IN CHAR16 *pDisplayValues ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TempReturnCode = EFI_SUCCESS; UINT32 Index = 0; REGION_GOAL_PER_DIMM_INFO *pCurrentGoal = NULL; CHAR16 *pSettingsString = NULL; CHAR16 *pStatusString = NULL; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; CHAR16 *pCapacityStr = NULL; CHAR16 *pPath = NULL; ZeroMem(DimmStr, sizeof(DimmStr)); if (pRegionConfigsInfo == NULL || (DisplayOptionSet && pDisplayValues == NULL)) { goto Finish; } for (Index = 0; Index < RegionConfigsCount; ++Index) { pCurrentGoal = &pRegionConfigsInfo[Index]; PRINTER_BUILD_KEY_PATH(pPath, DS_CONFIG_GOAL_INDEX_PATH, Index); /* always print dimmID */ /** Dimm ID **/ ReturnCode = GetPreferredDimmIdAsString(pCurrentGoal->DimmID, pCurrentGoal->DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } PRINTER_SET_KEY_VAL_WIDE_STR(pCmd->pPrintCtx, pPath, DIMM_ID_STR, DimmStr); /** Socket Id **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayValues, SOCKET_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pCmd->pPrintCtx, pPath, SOCKET_ID_STR, FORMAT_HEX, pCurrentGoal->SocketId); } /** Volatile Size **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayValues, MEMORY_SIZE_PROPERTY))) { ReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, ROUNDDOWN(pCurrentGoal->VolatileSize, SIZE_1GB), CurrentUnits, TRUE, &pCapacityStr); PRINTER_SET_KEY_VAL_WIDE_STR(pCmd->pPrintCtx, pPath, MEMORY_SIZE_PROPERTY, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); } /** AppDirect1Size **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayValues, APPDIRECT_1_SIZE_PROPERTY))) { TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pCurrentGoal->AppDirectSize[0], CurrentUnits, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pCmd->pPrintCtx, pPath, APPDIRECT_1_SIZE_PROPERTY, FORMAT_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); } /** AppDirect1Index **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayValues, APPDIRECT_1_INDEX_PROPERTY))) { if (pCurrentGoal->AppDirectSize[0] == 0) { PRINTER_SET_KEY_VAL_WIDE_STR(pCmd->pPrintCtx, pPath, APPDIRECT_1_INDEX_PROPERTY, NA_STR); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pCmd->pPrintCtx, pPath, APPDIRECT_1_INDEX_PROPERTY, FORMAT_INT32, pCurrentGoal->AppDirectIndex[0]); } } /** AppDirect1Settings **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayValues, APPDIRECT_1_SETTINGS_PROPERTY))) { InterleaveSettingsToString(pCurrentGoal->AppDirectSize[0], pCurrentGoal->NumberOfInterleavedDimms[0], pCurrentGoal->ImcInterleaving[0], pCurrentGoal->ChannelInterleaving[0], &pSettingsString); PRINTER_SET_KEY_VAL_WIDE_STR(pCmd->pPrintCtx, pPath, APPDIRECT_1_SETTINGS_PROPERTY, pSettingsString); FREE_POOL_SAFE(pSettingsString); } /** AppDirect2Size **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayValues, APPDIRECT_2_SIZE_PROPERTY))) { TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pCurrentGoal->AppDirectSize[1], CurrentUnits, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pCmd->pPrintCtx, pPath, APPDIRECT_2_SIZE_PROPERTY, FORMAT_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); } /** AppDirect2Index **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayValues, APPDIRECT_2_INDEX_PROPERTY))) { if (pCurrentGoal->AppDirectSize[1] == 0) { PRINTER_SET_KEY_VAL_WIDE_STR(pCmd->pPrintCtx, pPath, APPDIRECT_2_INDEX_PROPERTY, NA_STR); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pCmd->pPrintCtx, pPath, APPDIRECT_2_INDEX_PROPERTY, FORMAT_INT32, pCurrentGoal->AppDirectIndex[1]); } } /** AppDirect2Settings **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayValues, APPDIRECT_2_SETTINGS_PROPERTY))) { InterleaveSettingsToString(pCurrentGoal->AppDirectSize[1], pCurrentGoal->NumberOfInterleavedDimms[1], pCurrentGoal->ImcInterleaving[1], pCurrentGoal->ChannelInterleaving[1], &pSettingsString); PRINTER_SET_KEY_VAL_WIDE_STR(pCmd->pPrintCtx, pPath, APPDIRECT_2_SETTINGS_PROPERTY, pSettingsString); FREE_POOL_SAFE(pSettingsString); } /** Status **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayValues, STATUS_STR))) { pStatusString = GoalStatusToString(gNvmDimmCliHiiHandle, pCurrentGoal->Status); PRINTER_SET_KEY_VAL_WIDE_STR(pCmd->pPrintCtx, pPath, STATUS_STR, pStatusString); FREE_POOL_SAFE(pStatusString); } } Finish: //Specify table attributes PRINTER_CONFIGURE_DATA_ATTRIBUTES(pCmd->pPrintCtx, DS_ROOT_PATH, &ShowGoalDataSetAttribs); FREE_POOL_SAFE(pPath); return ReturnCode; } /** Execute the Show Goal command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS ShowGoal( IN struct Command *pCmd ) { BOOLEAN AllOptionSet = FALSE; BOOLEAN DisplayOptionSet = FALSE; UINT16 *pDimmIds = NULL; UINT16 *pSocketIds = NULL; UINT16 UnitsOption = DISPLAY_SIZE_UNIT_UNKNOWN; UINT16 UnitsToDisplay = FixedPcdGet16(PcdDcpmmCliDefaultCapacityUnit); UINT32 DimmIdsCount = 0; UINT32 SocketIdsCount = 0; UINT32 RegionConfigsCount = 0; CHAR16 *pSettingsString = NULL; CHAR16 *pDisplayValues = NULL; CHAR16 *pTargetValue = NULL; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; COMMAND_STATUS *pCommandStatus = NULL; REGION_GOAL_PER_DIMM_INFO RegionConfigsInfo[MAX_DIMMS]; EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; DISPLAY_PREFERENCES DisplayPreferences; PRINT_CONTEXT *pPrinterCtx = NULL; CMD_DISPLAY_OPTIONS *pDispOptions = NULL; NVDIMM_ENTRY(); SetMem(RegionConfigsInfo, sizeof(RegionConfigsInfo), 0x0); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; pDispOptions = AllocateZeroPool(sizeof(CMD_DISPLAY_OPTIONS)); if (NULL == pDispOptions) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = CheckAllAndDisplayOptions(pCmd, mppAllowedShowGoalDisplayValues, ALLOWED_DISP_VALUES_COUNT(mppAllowedShowGoalDisplayValues), pDispOptions); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CheckAllAndDisplayOptions has returned error. Code " FORMAT_EFI_STATUS "\n", ReturnCode); goto Finish; } pDisplayValues = pDispOptions->pDisplayValues; AllOptionSet = pDispOptions->AllOptionSet; DisplayOptionSet = pDispOptions->DisplayOptionSet; ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } /** Need NvmDimmConfigProtocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } // check targets if (ContainTarget(pCmd, DIMM_TARGET)) { pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetDimmIdsFromString"); goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsCount)){ ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } } // check sockets if (ContainTarget(pCmd, SOCKET_TARGET)) { pTargetValue = GetTargetValue(pCmd, SOCKET_TARGET); ReturnCode = GetUintsFromString(pTargetValue, &pSocketIds, &SocketIdsCount); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_TARGET_SOCKET); NVDIMM_DBG("Failed on GetUintsFromString"); goto Finish; } } ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } UnitsToDisplay = DisplayPreferences.SizeUnit; ReturnCode = GetUnitsOption(pCmd, &UnitsOption); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Any valid units option will override the preferences **/ if (UnitsOption != DISPLAY_SIZE_UNIT_UNKNOWN) { UnitsToDisplay = UnitsOption; } if (ContainsValue(pDisplayValues, APPDIRECT_1_INDEX_PROPERTY) && ContainsValue(pDisplayValues, APPDIRECT_INDEX_PROPERTY)) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_WARN("Values used together"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_VALUES_APPDIRECT_INDICES_USED_TOGETHER); goto Finish; } if (ContainsValue(pDisplayValues, APPDIRECT_1_SIZE_PROPERTY) && ContainsValue(pDisplayValues, APPDIRECT_SIZE_PROPERTY)) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_WARN("Values used together"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_VALUES_APPDIRECT_SIZE_USED_TOGETHER); goto Finish; } /** Fetch goal configs from driver **/ ReturnCode = pNvmDimmConfigProtocol->GetGoalConfigs( pNvmDimmConfigProtocol, pDimmIds, DimmIdsCount, pSocketIds, SocketIdsCount, MAX_DIMMS, RegionConfigsInfo, &RegionConfigsCount, pCommandStatus); if (EFI_ERROR(ReturnCode)) { if (EFI_VOLUME_CORRUPTED == ReturnCode) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_PCD_CORRUPTED); } ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PrinterSetCommandStatus(pPrinterCtx, ReturnCode, CLI_GET_REGION_MSG, CLI_GET_REGION_ON_MSG, pCommandStatus); goto Finish; } if (RegionConfigsCount == 0) { //WA, to ensure ESX prints a message when no entries are found. if (PRINTER_ESX_FORMAT_ENABLED(pPrinterCtx)) { if (SocketIdsCount > 0) { PRINTER_SET_MSG(pPrinterCtx, EFI_NOT_FOUND, CLI_NO_GOALS_ON_SOCKET_MSG); } else { PRINTER_SET_MSG(pPrinterCtx, EFI_NOT_FOUND, CLI_NO_GOALS_MSG); } } else { if (SocketIdsCount > 0) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_NO_GOALS_ON_SOCKET_MSG); } else { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_NO_GOALS_MSG); } } goto Finish; } if (!DisplayOptionSet && !AllOptionSet) { /** Default table view **/ ReturnCode = ShowGoalPrintTableView(pCmd, RegionConfigsInfo, UnitsToDisplay, RegionConfigsCount, TRUE); } else { /** Detailed view **/ ReturnCode = ShowGoalPrintDetailedView(pCmd, RegionConfigsInfo, RegionConfigsCount, AllOptionSet, DisplayOptionSet, UnitsToDisplay, pDisplayValues); } if (!EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_REBOOT_REQUIRED_MSG); } Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pSocketIds); FREE_POOL_SAFE(pSettingsString); FREE_CMD_DISPLAY_OPTIONS_SAFE(pDispOptions); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the Show Goal command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowGoalCommand( ) { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowGoalCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowGoalCommand.h000066400000000000000000000044731440615110200211120ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_GOAL_COMMAND_ #define _SHOW_GOAL_COMMAND_ #define APPDIRECT_1_SIZE_TABLE_HEADER L"AppDirect1Size" #define APPDIRECT_2_SIZE_TABLE_HEADER L"AppDirect2Size" #define MEMORY_SIZE_TABLE_HEADER L"MemorySize" #define SOCKET_ID_TABLE_HEADER L"SocketID" #define DIMM_ID_TABLE_HEADER L"DimmID" #define STATUS_STR L"Status" #define CLI_REBOOT_REQUIRED_MSG L"A reboot is required to process new memory allocation goals.\n" #define CLI_NO_GOALS_MSG L"There are no goal configs defined in the system.\nPlease use 'show -region' to display currently valid persistent memory regions.\n" #define CLI_NO_GOALS_ON_SOCKET_MSG L"There are no goal configs defined on the specified socket.\nPlease use 'show -region' to display currently valid persistent memory regions.\n" #define CLI_GET_REGION_MSG L"Get region configuration goal" #define CLI_GET_REGION_ON_MSG L" on" #define CLI_CREATE_SUCCESS_STATUS L"Created following region configuration goal\n" #include /** Register the Show Goal command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowGoalCommand(); /** Print results of show goal according to table view @param[in] pCmd command from CLI @param[in] pRegionConfigsInfo - Region Config table to be printed @param[in] CurrentUnits The requested type of units to convert the capacity into @param[in] RegionConfigsCount - Number of elements in array @retval EFI_SUCCESS if printing is successful @retval EFI_INVALID_PARAMETER if input parameter is incorrect **/ EFI_STATUS ShowGoalPrintTableView( IN struct Command *pCmd, IN REGION_GOAL_PER_DIMM_INFO *pRegionConfigsInfo, IN UINT16 CurrentUnits, IN UINT32 RegionConfigsCount, IN BOOLEAN Buffered ); /** Execute the Show Goal command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS ShowGoal( IN struct Command *pCmd ); #endif /** _SHOW_GOAL_COMMAND_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowMemoryResourcesCommand.c000066400000000000000000000334031440615110200233610ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include "ShowMemoryResourcesCommand.h" #include #include #include #include #include #include #include #include "Common.h" #include "NvmDimmCli.h" #include #define DS_MEMORY_RESOURCES_PATH L"/MemoryResources" #define DS_MEMORY_RESOURCES_DATA_PATH L"/MemoryResources/data" #define DS_MEMORY_RESOURCES_DATA_INDEX_PATH L"/MemoryResources/data[%d]" /* * PRINTER TABLE ATTRIBUTES (5 columns) * | DDR | PMemModule | Total | * =========================================================================== * Volatile | Volatile DDR Mem | Volatile PMem | Volatile Mem | * AppDirect | N/A | AppDirect Mem | AppDirect Mem | * Cache | DDR Cache Mem | N/A | Cache Mem | * Inaccessible | N/A | Inaccessible Mem | Inaccessible Mem | * Physical | Total DDR Mem | Total DCPMM Mem | Total Mem | */ PRINTER_TABLE_ATTRIB ShowMemoryResourcesTableAttributes = { { { MEMORY_TYPE_STR, //COLUMN HEADER MEMORY_TYPE_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_MEMORY_RESOURCES_DATA_PATH PATH_KEY_DELIM MEMORY_TYPE_STR //COLUMN DATA PATH }, { DDR_STR, //COLUMN HEADER DDR_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_MEMORY_RESOURCES_DATA_PATH PATH_KEY_DELIM DDR_STR //COLUMN DATA PATH }, { PMEM_MODULE_PASCAL_CASE_STR, //COLUMN HEADER PMEM_MODULE_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_MEMORY_RESOURCES_DATA_PATH PATH_KEY_DELIM PMEM_MODULE_PASCAL_CASE_STR //COLUMN DATA PATH }, { TOTAL_STR, //COLUMN HEADER TOTAL_STR_WIDTH, //COLUMN MAX STR WIDTH DS_MEMORY_RESOURCES_DATA_PATH PATH_KEY_DELIM TOTAL_STR //COLUMN DATA PATH } } }; PRINTER_DATA_SET_ATTRIBS ShowMemoryResourcesDataSetAttribsPmtt3 = { NULL, &ShowMemoryResourcesTableAttributes }; /** Command syntax definition **/ struct Command ShowMemoryResourcesCommand = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {UNITS_OPTION_SHORT, UNITS_OPTION, L"", UNITS_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_UNIT_DETAILS_TEXT, FALSE, ValueRequired } #endif }, {{MEMORY_RESOURCES_TARGET, L"", L"", TRUE, ValueEmpty}}, //!< targets {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show memory allocation information for this platform.", //!< help ShowMemoryResources, TRUE, //!< enable print control support }; PRINTER_DATA_SET_ATTRIBS ShowMemResourcesDataSetAttribs = { NULL, NULL }; /** Execute the show memory resources command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS ShowMemoryResources( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TempReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; MEMORY_RESOURCES_INFO MemoryResourcesInfo; UINT16 UnitsOption = DISPLAY_SIZE_UNIT_UNKNOWN; UINT16 UnitsToDisplay = FixedPcdGet16(PcdDcpmmCliDefaultCapacityUnit); UINT64 DcpmmInaccessibleCapacity = 0; UINT64 TotalCapacity = 0; CHAR16 *pCapacityStr = NULL; CHAR16 *pPcdMissingStr = NULL; DISPLAY_PREFERENCES DisplayPreferences; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; UINT32 Index = 0; NVDIMM_ENTRY(); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); SetMem(&MemoryResourcesInfo, sizeof(MemoryResourcesInfo), 0x0); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } UnitsToDisplay = DisplayPreferences.SizeUnit; ReturnCode = GetUnitsOption(pCmd, &UnitsOption); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Any valid units option will override the preferences **/ if (UnitsOption != DISPLAY_SIZE_UNIT_UNKNOWN) { UnitsToDisplay = UnitsOption; } /** Make sure we can access the config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { Print(FORMAT_STR_NL, CLI_ERR_OPENING_CONFIG_PROTOCOL); ReturnCode = EFI_NOT_FOUND; goto Finish; } // Ignore errors from this! Always print what we can ReturnCode = pNvmDimmConfigProtocol->GetMemoryResourcesInfo(pNvmDimmConfigProtocol, &MemoryResourcesInfo); if (EFI_ERROR(ReturnCode)) { if (MemoryResourcesInfo.PcdInvalid) { pPcdMissingStr = HiiGetString(gNvmDimmCliHiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PCD_CURR_CONF_MISSING), NULL); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, pPcdMissingStr); } // Ignore errors. Always print what we can ReturnCode = EFI_SUCCESS; } /* Print Volatile Capacities */ Index = 0; PRINTER_BUILD_KEY_PATH(pPath, DS_MEMORY_RESOURCES_DATA_INDEX_PATH, Index); // Print Header PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEMORY_TYPE_STR, L"Volatile"); // Print DDR Memory/Volatile Capacity TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, MemoryResourcesInfo.DDRVolatileCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DDR_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); // Print PMem module Volatile Capacity TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, MemoryResourcesInfo.VolatileCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, PMEM_MODULE_PASCAL_CASE_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); // Print Total Volatile Capacity if (MemoryResourcesInfo.DDRVolatileCapacity == ACPI_TABLE_VALUE_UNKNOWN || MemoryResourcesInfo.VolatileCapacity == ACPI_TABLE_VALUE_UNKNOWN) { TotalCapacity = ACPI_TABLE_VALUE_UNKNOWN; } else { TotalCapacity = MemoryResourcesInfo.DDRVolatileCapacity + MemoryResourcesInfo.VolatileCapacity; } TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, TotalCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, TOTAL_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); /* Print App Direct Capacities */ Index = 1; PRINTER_BUILD_KEY_PATH(pPath, DS_MEMORY_RESOURCES_DATA_INDEX_PATH, Index); // Print Header PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEMORY_TYPE_STR, L"AppDirect"); // Print DDR App Direct Capacities (as of now, this is N/A) PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DDR_STR, DASH_STR); // Print PMem module DDR App Direct Capacities TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, MemoryResourcesInfo.AppDirectCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, PMEM_MODULE_PASCAL_CASE_STR, pCapacityStr); // Print Total App Direct Capacities PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, TOTAL_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); /* Print DDR Cache Capacities */ Index = 2; PRINTER_BUILD_KEY_PATH(pPath, DS_MEMORY_RESOURCES_DATA_INDEX_PATH, Index); // Print header PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEMORY_TYPE_STR, L"Cache"); TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, MemoryResourcesInfo.DDRCacheCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); // Print DDR Cache Capacity PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DDR_STR, pCapacityStr); // Print PMem module Cache Capacity PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, PMEM_MODULE_PASCAL_CASE_STR, DASH_STR); // Print Total Cache Capacity PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, TOTAL_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); Index = 3; PRINTER_BUILD_KEY_PATH(pPath, DS_MEMORY_RESOURCES_DATA_INDEX_PATH, Index); // Print Header PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEMORY_TYPE_STR, L"Inaccessible"); // Print DDR Inaccessible Capacity TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, MemoryResourcesInfo.DDRInaccessibleCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DDR_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); // Print PMem module Inaccessible Capacity if (MemoryResourcesInfo.InaccessibleCapacity == ACPI_TABLE_VALUE_UNKNOWN || MemoryResourcesInfo.ReservedCapacity == ACPI_TABLE_VALUE_UNKNOWN || MemoryResourcesInfo.UnconfiguredCapacity == ACPI_TABLE_VALUE_UNKNOWN) { DcpmmInaccessibleCapacity = ACPI_TABLE_VALUE_UNKNOWN; } else { DcpmmInaccessibleCapacity = MemoryResourcesInfo.InaccessibleCapacity + MemoryResourcesInfo.ReservedCapacity + MemoryResourcesInfo.UnconfiguredCapacity; } TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, DcpmmInaccessibleCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, PMEM_MODULE_PASCAL_CASE_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); // Print Total Inaccessible Capacity // If one of the elements is unknown, then the total should be unknown too if (MemoryResourcesInfo.DDRInaccessibleCapacity == ACPI_TABLE_VALUE_UNKNOWN || DcpmmInaccessibleCapacity == ACPI_TABLE_VALUE_UNKNOWN) { TotalCapacity = ACPI_TABLE_VALUE_UNKNOWN; } else { TotalCapacity = MemoryResourcesInfo.DDRInaccessibleCapacity + DcpmmInaccessibleCapacity; } TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, TotalCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, TOTAL_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); /* Print Physical Capacities */ Index = 4; PRINTER_BUILD_KEY_PATH(pPath, DS_MEMORY_RESOURCES_DATA_INDEX_PATH, Index); // Print header PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEMORY_TYPE_STR, L"Physical"); // Print DDR Physical Capacity ReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, MemoryResourcesInfo.DDRRawCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DDR_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); // Print PMem module Physical Capacity ReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, MemoryResourcesInfo.RawCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, PMEM_MODULE_PASCAL_CASE_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); // Print Total Physical Capacity // If one of the elements is unknown, then the total should be unknown too if (MemoryResourcesInfo.DDRRawCapacity == ACPI_TABLE_VALUE_UNKNOWN || MemoryResourcesInfo.RawCapacity == ACPI_TABLE_VALUE_UNKNOWN) { TotalCapacity = ACPI_TABLE_VALUE_UNKNOWN; } else { TotalCapacity = MemoryResourcesInfo.DDRRawCapacity + MemoryResourcesInfo.RawCapacity; } ReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, TotalCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, TOTAL_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); Finish: PRINTER_ENABLE_TEXT_TABLE_FORMAT(pPrinterCtx); PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_MEMORY_RESOURCES_PATH, &ShowMemoryResourcesDataSetAttribsPmtt3); PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pPath); FREE_POOL_SAFE(pPcdMissingStr); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the show memory resources command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowMemoryResourcesCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowMemoryResourcesCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowMemoryResourcesCommand.h000066400000000000000000000024171440615110200233670ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SRC_CLI_SHOW_MEMORY_RESOURCES_COMMAND_H_ #define _SRC_CLI_SHOW_MEMORY_RESOURCES_COMMAND_H_ #include "CommandParser.h" #define DISPLAYED_CAPACITY_STR L"Capacity" #define DISPLAYED_MEMORY_CAPACITY_STR L"MemoryCapacity" #define DISPLAYED_APPDIRECT_CAPACITY_STR L"AppDirectCapacity" #define DISPLAYED_UNCONFIGURED_CAPACITY_STR L"UnconfiguredCapacity" #define DISPLAYED_INACCESSIBLE_CAPACITY_STR L"InaccessibleCapacity" #define DISPLAYED_RESERVED_CAPACITY_STR L"ReservedCapacity" /** Execute the show memory resources command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS ShowMemoryResources( IN struct Command *pCmd ); /** Register the show memory resources command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowMemoryResourcesCommand( ); #endif /* _SRC_CLI_SHOW_MEMORY_RESOURCES_COMMAND_H_ */ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowNamespaceCommand.h000066400000000000000000000027141440615110200221200ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_NAMESPACE_COMMAND_H_ #define _SHOW_NAMESPACE_COMMAND_H_ #include #include #include #include #include #include #include #include "Common.h" #include #define NAMESPACE_ID_STR L"NamespaceId" #define NAME_STR L"Name" #define CAPACITY_STR L"Capacity" #define HEALTH_STATE_STR L"HealthState" #define REGION_ID_STR L"RegionID" #define BLOCK_SIZE_STR L"BlockSize" #define MODE_STR L"Mode" #define LABEL_VERSION_STR L"LabelVersion" #define NAMESPACE_GUID_STR L"NamespaceGuid" #define REGION_ID_STR L"RegionID" /** Register the show namespace command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowNamespaceCommand( ); /** Execute the show namespace command @param[in] pCmd command from CLI @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS ShowNamespace( IN struct Command *pCmd ); #endif /* _SHOW_NAMESPACE_COMMAND_H_ */ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowPcdCommand.c000066400000000000000000001337201440615110200207270ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include "CommandParser.h" #include "Common.h" #include "ShowPcdCommand.h" #include #include /** Command syntax definition **/ struct Command ShowPcdCommand = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"", HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", LARGE_PAYLOAD_OPTION, L"", L"", HELP_LPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", SMALL_PAYLOAD_OPTION, L"", L"", HELP_SPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #else {L"", L"", L"", L"",L"", FALSE, ValueOptional} #endif }, { //!< targets {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional}, {PCD_TARGET, L"", PCD_CONFIG_TARGET_VALUE L"|" PCD_LSA_TARGET_VALUE, TRUE, ValueOptional} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show platform configuration data (PCD) stored on one or more " PMEM_MODULES_STR L".", //!< help ShowPcd, TRUE }; #define DS_ROOT_PATH L"/PcdList" #define DS_DIMM_PATH L"/PcdList/Dimm" #define DS_DIMM_INDEX_PATH L"/PcdList/Dimm[%d]" #define DS_PCD_PATH L"/PcdList/Dimm/Pcd" #define DS_PCD_INDEX_PATH L"/PcdList/Dimm[%d]/Pcd[%d]" #define DS_TABLE_PATH L"/PcdList/Dimm/Pcd/Table" #define DS_TABLE_INDEX_PATH L"/PcdList/Dimm[%d]/Pcd[%d]/Table[%d]" #define DS_PCD_PCAT_TABLE_INDEX_PATH L"/PcdList/Dimm[%d]/Pcd[%d]/Table[%d]/PcatTable[%d]" #define DS_IDENTIFICATION_INFO_INDEX_PATH L"/PcdList/Dimm[%d]/Pcd[%d]/Table[%d]/PcatTable[%d]/IdentificationInfoTable[%d]" #define PCD_TARGET_CONFIG_STR L"Target cfg " #define PCD_TARGET_NAMESPACE_STR L"Target namespace " #define PCD_TABLE_STR L"Table" #define PCD_STR L"Pcd" #define PCD_PCAT_TABLE_STR L"PcatTable" #define PCD_IDENTIFICATION_INFO_STR L"IdentificationInfoTable" #define COLUMN_IN_HEX_DUMP 16 CHAR16 *gpPathLba = NULL; PRINT_CONTEXT *gpPrinterCtxLbaCommon = NULL; UINT16 gDimmIndex = 0; UINT8 ConfigIndex = 0; PRINTER_LIST_ATTRIB ShowPcdListAttributes = { { { DIMM_NODE_STR, //GROUP LEVEL TYPE L"--" DIMM_ID_STR L":$(" DIMM_ID_STR L")--", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT FORMAT_STR L": " FORMAT_STR, //NULL or KEY VAL FORMAT STR DIMM_ID_STR //NULL or IGNORE KEY LIST (K1;K2) }, { PCD_STR, //GROUP LEVEL TYPE L"--" PCD_STR L":$(" PCD_STR L")--", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT FORMAT_STR L": " FORMAT_STR, //NULL or KEY VAL FORMAT STR PCD_STR //NULL or IGNORE KEY LIST (K1;K2) }, { PCD_TABLE_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT L"---" PCD_TABLE_STR L": $(" PCD_TABLE_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR L": " FORMAT_STR, //NULL or KEY VAL FORMAT STR PCD_TABLE_STR //NULL or IGNORE KEY LIST (K1;K2) }, { PCD_PCAT_TABLE_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT SHOW_LIST_IDENT PCD_PCAT_TABLE_STR SHOW_LIST_IDENT L": $(" PCD_PCAT_TABLE_STR L")", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR L": " FORMAT_STR, //NULL or KEY VAL FORMAT STR PCD_PCAT_TABLE_STR //NULL or IGNORE KEY LIST (K1;K2) }, { PCD_IDENTIFICATION_INFO_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT SHOW_LIST_IDENT PCD_IDENTIFICATION_INFO_STR SHOW_LIST_IDENT L": $(" PCD_IDENTIFICATION_INFO_STR L")", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR L": " FORMAT_STR, //NULL or KEY VAL FORMAT STR PCD_IDENTIFICATION_INFO_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; PRINTER_DATA_SET_ATTRIBS ShowPcdDataSetAttribs = { &ShowPcdListAttributes, NULL }; /** Register the Show PCD command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowPcdCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowPcdCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } STATIC EFI_STATUS GetPcdTarget( IN CHAR16 *pTargetValue, OUT UINT8 *pPcdTarget ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pTargetValue == NULL || pPcdTarget == NULL) { goto Finish; } if (StrLen(pTargetValue) == 0) { *pPcdTarget = PCD_TARGET_ALL; } else if (StrICmp(pTargetValue, PCD_CONFIG_TARGET_VALUE) == 0) { *pPcdTarget = PCD_TARGET_CONFIG; } else if (StrICmp(pTargetValue, PCD_LSA_TARGET_VALUE) == 0) { *pPcdTarget = PCD_TARGET_NAMESPACES; } else { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Execute the Show PCD command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_FOUND couldn't open Config Protocol @retval EFI_ABORTED internal **/ EFI_STATUS ShowPcd( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; COMMAND_STATUS *pCommandStatus = NULL; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; DIMM_PCD_INFO *pDimmPcdInfo = NULL; UINT32 DimmPcdInfoCount = 0; UINT16 *pDimmIds = NULL; UINT32 DimmIdsCount = 0; CHAR16 *pTargetValue = NULL; UINT8 PcdTarget = PCD_TARGET_ALL; UINT32 Index = 0; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; NVDIMM_ENTRY(); SetDisplayInfo(L"ShowPcd", ResultsView, NULL); ZeroMem(DimmStr, sizeof(DimmStr)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } /** Printing will still work via compatibility mode if NULL so no need to check for NULL. **/ pPrinterCtx = pCmd->pPrintCtx; /** Get config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_DIMMS); } goto Finish; } ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (ContainTarget(pCmd, DIMM_TARGET)) { pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsCount); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsCount)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } } pTargetValue = GetTargetValue(pCmd, PCD_TARGET); ReturnCode = GetPcdTarget(pTargetValue, &PcdTarget); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_INCORRECT_VALUE_TARGET_PCD); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetPcd(pNvmDimmConfigProtocol, PcdTarget, pDimmIds, DimmIdsCount, &pDimmPcdInfo, &DimmPcdInfoCount, pCommandStatus); if (EFI_ERROR(ReturnCode)) { ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); goto Finish; } for (Index = 0; Index < DimmPcdInfoCount; Index++) { ReturnCode = GetPreferredDimmIdAsString(pDimmPcdInfo[Index].DimmId, pDimmPcdInfo[Index].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_INDEX_PATH, Index); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, DimmStr); if (PcdTarget == PCD_TARGET_ALL || PcdTarget == PCD_TARGET_NAMESPACES) { PRINTER_BUILD_KEY_PATH(pPath, DS_PCD_INDEX_PATH, Index, 0); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, PCD_STR, L"LSA"); PrintLabelStorageArea(pDimmPcdInfo[Index].pLabelStorageArea, pPrinterCtx, pPath); } if (PcdTarget == PCD_TARGET_ALL || PcdTarget == PCD_TARGET_CONFIG) { PRINTER_BUILD_KEY_PATH(pPath, DS_PCD_INDEX_PATH, Index, 1); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, PCD_STR, L"Config"); PrintPcdConfigurationHeader(pDimmPcdInfo[Index].pConfHeader, pPrinterCtx, pPath); } gDimmIndex++; } ReturnCode = EFI_SUCCESS; PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowPcdDataSetAttribs); PRINTER_ENABLE_LIST_TABLE_FORMAT(pPrinterCtx); pPrinterCtx->DoNotPrintGeneralStatusSuccessCode = TRUE; Finish: PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, CLI_INFO_SHOW_PCD, CLI_INFO_ON, pCommandStatus); PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FreeCommandStatus(&pCommandStatus); FreeDimmPcdInfoArray(pDimmPcdInfo, DimmPcdInfoCount); pDimmPcdInfo = NULL; FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pPath); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Print Platform Config Data table header @param[in] pHeader table header @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdTableHeader( IN TABLE_HEADER *pHeader, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Signature ", L"%c%c%c%c", pHeader->Signature & 0xFF, (pHeader->Signature >> 8) & 0xFF, (pHeader->Signature >> 16) & 0xFF, (pHeader->Signature >> 24) & 0xFF); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Length ", FORMAT_HEX_NOWIDTH, pHeader->Length); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Revision ", FORMAT_HEX_NOWIDTH, pHeader->Revision); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Checksum ", FORMAT_HEX_NOWIDTH, pHeader->Checksum); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"OemId ", L"%c%c%c%c%c%c", pHeader->OemId[0], pHeader->OemId[1], pHeader->OemId[2], pHeader->OemId[3], pHeader->OemId[4], pHeader->OemId[5]); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"OemTableId ", L"%c%c%c%c%c%c", ((UINT8 *)&pHeader->OemTableId)[0], ((UINT8 *)&pHeader->OemTableId)[1], ((UINT8 *)&pHeader->OemTableId)[2], ((UINT8 *)&pHeader->OemTableId)[3], ((UINT8 *)&pHeader->OemTableId)[4], ((UINT8 *)&pHeader->OemTableId)[5]); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"OemRevision ", FORMAT_HEX_NOWIDTH, pHeader->OemRevision); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"CreatorId ", L"%c%c%c%c", ((UINT8 *)&pHeader->CreatorId)[0], ((UINT8 *)&pHeader->CreatorId)[1], ((UINT8 *)&pHeader->CreatorId)[2], ((UINT8 *)&pHeader->CreatorId)[3]); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"CreatorRevision ", FORMAT_HEX_NOWIDTH, pHeader->CreatorRevision); } /** Print Platform Config Data PCAT table header @param[in] pHeader PCAT table header @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdPcatTableHeader( IN PCAT_TABLE_HEADER *pHeader, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Type ", FORMAT_HEX_NOWIDTH, pHeader->Type); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Length ", FORMAT_HEX_NOWIDTH, pHeader->Length); } /** Print Platform Config Data Partition Size Change table @param[in] pPartitionSizeChange Partition Size Change table @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdPartitionSizeChange( IN NVDIMM_PARTITION_SIZE_CHANGE *pPartitionSizeChange, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PCD_PCAT_TABLE_STR, L"PCD Partition Size Change"); PrintPcdPcatTableHeader(&pPartitionSizeChange->Header, pPrinterCtx, pPath); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"PartitionSizeChangeStatus ", FORMAT_HEX_NOWIDTH, pPartitionSizeChange->PartitionSizeChangeStatus); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"PartitionSize ", FORMAT_UINT64_HEX_NOWIDTH, pPartitionSizeChange->PmPartitionSize); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"PartitionSize ", L"\n"); } /** Print Platform Config Data Identification Information table @param[in] pIdentificationInfo Identification Information table @param[in] PcdConfigTableRevision Revision of the PCD Config tables @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdIdentificationInformation( IN VOID *pIdentificationInfoTable, IN ACPI_REVISION PcdConfigTableRevision, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ) { CHAR16 PartNumber[PART_NUMBER_SIZE + 1]; CHAR16 *pTmpDimmUid = NULL; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, PCD_IDENTIFICATION_INFO_STR, L"PCD Identification Info"); if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcdConfigTableRevision)) { NVDIMM_IDENTIFICATION_INFORMATION *pIdentificationInfo = (NVDIMM_IDENTIFICATION_INFORMATION *)pIdentificationInfoTable; ZeroMem(PartNumber, sizeof(PartNumber)); AsciiStrToUnicodeStrS(pIdentificationInfo->DimmIdentification.Version1.DimmPartNumber, PartNumber, PART_NUMBER_SIZE + 1); if (IS_ACPI_REV_MAJ_0_MIN_1(PcdConfigTableRevision)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"DimmManufacturerId ", FORMAT_HEX_NOWIDTH, EndianSwapUint16(pIdentificationInfo->DimmIdentification.Version1.DimmManufacturerId)); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"DimmSerialNumber ", FORMAT_HEX_NOWIDTH, EndianSwapUint32(pIdentificationInfo->DimmIdentification.Version1.DimmSerialNumber)); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"DimmPartNumber ", FORMAT_STR, PartNumber); } else { pTmpDimmUid = CatSPrint(NULL, L"%04x-%02x-%04x-%08x", EndianSwapUint16(pIdentificationInfo->DimmIdentification.Version2.Uid.ManufacturerId), pIdentificationInfo->DimmIdentification.Version2.Uid.ManufacturingLocation, EndianSwapUint16(pIdentificationInfo->DimmIdentification.Version2.Uid.ManufacturingDate), EndianSwapUint32(pIdentificationInfo->DimmIdentification.Version2.Uid.SerialNumber)); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"DimmUniqueIdentifer ", FORMAT_STR, pTmpDimmUid); } PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"PartitionOffset ", FORMAT_UINT64_HEX_NOWIDTH, pIdentificationInfo->PartitionOffset); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"PmPartitionSize ", FORMAT_UINT64_HEX_NOWIDTH L"\n", pIdentificationInfo->PmPartitionSize); } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdConfigTableRevision)) { NVDIMM_IDENTIFICATION_INFORMATION3 *pIdentificationInfo = (NVDIMM_IDENTIFICATION_INFORMATION3 *)pIdentificationInfoTable; pTmpDimmUid = CatSPrint(NULL, L"%04x-%02x-%04x-%08x", EndianSwapUint16(pIdentificationInfo->DimmIdentification.ManufacturerId), pIdentificationInfo->DimmIdentification.ManufacturingLocation, EndianSwapUint16(pIdentificationInfo->DimmIdentification.ManufacturingDate), EndianSwapUint32(pIdentificationInfo->DimmIdentification.SerialNumber)); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"DimmUniqueIdentifer ", FORMAT_STR, pTmpDimmUid); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"DimmLocation ", FORMAT_UINT64_HEX_NOWIDTH, pIdentificationInfo->DimmLocation.AsUint64); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"PartitionOffset ", FORMAT_UINT64_HEX_NOWIDTH, pIdentificationInfo->PartitionOffset); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"PmPartitionSize ", FORMAT_UINT64_HEX_NOWIDTH L"\n", pIdentificationInfo->PmPartitionSize); } if (pTmpDimmUid != NULL) { FREE_POOL_SAFE(pTmpDimmUid); } } /** Print Platform Config Data Interleave Information table and its extension tables @param[in] pInterleaveInfo Interleave Information table @param[in] PcdConfigTableRevision Revision of the PCD Config tables @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdInterleaveInformation( IN PCAT_TABLE_HEADER *pInterleaveInfoTable, IN ACPI_REVISION PcdConfigTableRevision, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ) { UINT32 Index = 0; UINT32 IdentificationInfoIndex = 0; CHAR16 *pPathPcdIdentificationInfo = NULL; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PCD_PCAT_TABLE_STR, L"PCD Interleave Info"); if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcdConfigTableRevision)) { NVDIMM_INTERLEAVE_INFORMATION *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION *)pInterleaveInfoTable; PrintPcdPcatTableHeader(&pInterleaveInfo->Header, pPrinterCtx, pPath); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"InterleaveSetIndex ", FORMAT_HEX_NOWIDTH, pInterleaveInfo->InterleaveSetIndex); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"NumOfDimmsInInterleaveSet ", FORMAT_HEX_NOWIDTH, pInterleaveInfo->NumOfDimmsInInterleaveSet); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"InterleaveMemoryType ", FORMAT_HEX_NOWIDTH, pInterleaveInfo->InterleaveMemoryType); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"InterleaveFormatChannel ", FORMAT_HEX_NOWIDTH, pInterleaveInfo->InterleaveFormatChannel); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"InterleaveFormatImc ", FORMAT_HEX_NOWIDTH, pInterleaveInfo->InterleaveFormatImc); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"InterleaveFormatWays ", FORMAT_HEX_NOWIDTH, pInterleaveInfo->InterleaveFormatWays); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"InterleaveChangeStatus ", FORMAT_HEX_NOWIDTH L"\n", pInterleaveInfo->InterleaveChangeStatus); NVDIMM_IDENTIFICATION_INFORMATION *pCurrentIdentInfo = (NVDIMM_IDENTIFICATION_INFORMATION *)&pInterleaveInfo->pIdentificationInfoList; for (Index = 0; Index < pInterleaveInfo->NumOfDimmsInInterleaveSet; Index++) { PRINTER_BUILD_KEY_PATH(pPathPcdIdentificationInfo, DS_IDENTIFICATION_INFO_INDEX_PATH, gDimmIndex, 1, ConfigIndex, 1, Index); IdentificationInfoIndex++; PrintPcdIdentificationInformation(pCurrentIdentInfo, PcdConfigTableRevision, pPrinterCtx, pPathPcdIdentificationInfo); pCurrentIdentInfo++; } } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdConfigTableRevision)) { NVDIMM_INTERLEAVE_INFORMATION3 *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION3 *)pInterleaveInfoTable; PrintPcdPcatTableHeader(&pInterleaveInfo->Header, pPrinterCtx, pPath); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"InterleaveSetIndex ", FORMAT_HEX_NOWIDTH, pInterleaveInfo->InterleaveSetIndex); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"NumOfDimmsInInterleaveSet ", FORMAT_HEX_NOWIDTH, pInterleaveInfo->NumOfDimmsInInterleaveSet); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"InterleaveMemoryType ", FORMAT_HEX_NOWIDTH, pInterleaveInfo->InterleaveMemoryType); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"InterleaveSizeChannel ", FORMAT_HEX_NOWIDTH, pInterleaveInfo->InterleaveFormatChannel); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"InterleaveSizeImc ", FORMAT_HEX_NOWIDTH, pInterleaveInfo->InterleaveFormatImc); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"InterleaveChangeStatus ", FORMAT_HEX_NOWIDTH , pInterleaveInfo->InterleaveChangeStatus); NVDIMM_IDENTIFICATION_INFORMATION3 *pCurrentIdentInfo = (NVDIMM_IDENTIFICATION_INFORMATION3 *)&pInterleaveInfo->pIdentificationInfoList; for (Index = 0; Index < pInterleaveInfo->NumOfDimmsInInterleaveSet; Index++) { PRINTER_BUILD_KEY_PATH(pPathPcdIdentificationInfo, DS_IDENTIFICATION_INFO_INDEX_PATH, gDimmIndex, 1, ConfigIndex, 1, Index); IdentificationInfoIndex++; PrintPcdIdentificationInformation(pCurrentIdentInfo, PcdConfigTableRevision, pPrinterCtx, pPathPcdIdentificationInfo); pCurrentIdentInfo++; } } FREE_POOL_SAFE(pPathPcdIdentificationInfo); } VOID PrintPcdConfigManagementAttributesInformation( IN CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *pConfigManagementAttributesInfo, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ) { INTEL_DIMM_CONFIG *pIntelDIMMConfig = NULL; EFI_GUID IntelDimmConfigVariableGuid = INTEL_DIMM_CONFIG_VARIABLE_GUID; CHAR16 *pGuidStr = NULL; pGuidStr = GuidToStr(&pConfigManagementAttributesInfo->Guid); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Mngmt Table ", L"PCD Cfg Mngmt Attr Extension"); PrintPcdPcatTableHeader(&pConfigManagementAttributesInfo->Header, pPrinterCtx, pPath); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"VendorID ", FORMAT_HEX_NOWIDTH, pConfigManagementAttributesInfo->VendorId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"GUID ", FORMAT_STR_NL, pGuidStr); if (CompareGuid(&pConfigManagementAttributesInfo->Guid, &IntelDimmConfigVariableGuid)) { pIntelDIMMConfig = (INTEL_DIMM_CONFIG *)pConfigManagementAttributesInfo->pGuidData; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Revision ", FORMAT_INT32, pIntelDIMMConfig->Revision); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"ProvisionCapacityMode ", FORMAT_INT32, pIntelDIMMConfig->ProvisionCapacityMode); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"MemorySize ", FORMAT_INT32, pIntelDIMMConfig->MemorySize); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"PMType ", FORMAT_INT32, pIntelDIMMConfig->PMType); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"ProvisionNamespaceMode ", FORMAT_INT32, pIntelDIMMConfig->ProvisionNamespaceMode); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"NamespaceFlags ", FORMAT_INT32, pIntelDIMMConfig->NamespaceFlags); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"NamespaceLabelVersion ", FORMAT_INT32, pIntelDIMMConfig->NamespaceLabelVersion); } FREE_POOL_SAFE(pGuidStr); } /** Print Platform Config Data Current Config table and its PCAT tables @param[in] pCurrentConfig Current Config table @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdCurrentConfig( IN NVDIMM_CURRENT_CONFIG *pCurrentConfig, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ) { NVDIMM_PARTITION_SIZE_CHANGE *pPartSizeChange = NULL; CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *pConfigManagementAttributesInfo = NULL; PCAT_TABLE_HEADER *pCurPcatTable = NULL; UINT32 SizeOfPcatTables = 0; EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 *pPathPcdPcatTable = NULL; if (IS_ACPI_HEADER_REV_INVALID(pCurrentConfig)){ ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Error: Invalid revision value %d for PCD current config table.", pCurrentConfig->Header.Revision); return; } PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PCD_TABLE_STR, L"PCD Current Config"); PrintPcdTableHeader(&pCurrentConfig->Header, pPrinterCtx, pPath); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"ConfigStatus ", FORMAT_HEX_NOWIDTH, pCurrentConfig->ConfigStatus); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"VolatileMemSizeIntoSpa ", FORMAT_UINT64_HEX_NOWIDTH, pCurrentConfig->VolatileMemSizeIntoSpa); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"PersistentMemSizeIntoSpa ", FORMAT_UINT64_HEX_NOWIDTH, pCurrentConfig->PersistentMemSizeIntoSpa); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"PersistentMemSizeIntoSpa ", L"\n"); /** Check if there is at least one PCAT table **/ if (pCurrentConfig->Header.Length <= sizeof(*pCurrentConfig)) { return; } pCurPcatTable = (PCAT_TABLE_HEADER *)&pCurrentConfig->pPcatTables; SizeOfPcatTables = pCurrentConfig->Header.Length - (UINT32)((UINT8 *)pCurPcatTable - (UINT8 *)pCurrentConfig); /** Example of the use of the while loop condition PCAT table #1 offset: 0 size: 10 PCAT table #2 offset: 10 size: 5 Size of PCAT tables: 15 (10 + 5) Iteration #1: offset: 0 Iteration #2: offset: 10 Iteration #3: offset: 15 stop the loop: offset isn't less than size **/ while ((UINT32)((UINT8 *)pCurPcatTable - (UINT8 *)&pCurrentConfig->pPcatTables) < SizeOfPcatTables) { if (pCurPcatTable->Type == PCAT_TYPE_PARTITION_SIZE_CHANGE_TABLE) { PRINTER_BUILD_KEY_PATH(pPathPcdPcatTable, DS_PCD_PCAT_TABLE_INDEX_PATH, gDimmIndex, 1, ConfigIndex, 0); pPartSizeChange = (NVDIMM_PARTITION_SIZE_CHANGE *)pCurPcatTable; PrintPcdPartitionSizeChange(pPartSizeChange, pPrinterCtx, pPathPcdPcatTable); pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pPartSizeChange->Header.Length); } else if (pCurPcatTable->Type == PCAT_TYPE_INTERLEAVE_INFORMATION_TABLE) { PRINTER_BUILD_KEY_PATH(pPathPcdPcatTable, DS_PCD_PCAT_TABLE_INDEX_PATH, gDimmIndex, 1, ConfigIndex, 1); PrintPcdInterleaveInformation(pCurPcatTable, pCurrentConfig->Header.Revision, pPrinterCtx, pPathPcdPcatTable); pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pCurPcatTable->Length); } else if (pCurPcatTable->Type == PCAT_TYPE_CONFIG_MANAGEMENT_ATTRIBUTES_TABLE) { pConfigManagementAttributesInfo = (CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *)pCurPcatTable; pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pConfigManagementAttributesInfo->Header.Length); } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Error: wrong PCAT table type"); break; } FREE_POOL_SAFE(pPathPcdPcatTable); } } /** Print Platform Config Data Config Input table and its PCAT tables @param[in] pConfigInput Config Input table @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdConfInput( IN NVDIMM_PLATFORM_CONFIG_INPUT *pConfigInput, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ) { NVDIMM_PARTITION_SIZE_CHANGE *pPartSizeChange = NULL; CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *pConfigManagementAttributesInfo = NULL; PCAT_TABLE_HEADER *pCurPcatTable = NULL; UINT32 SizeOfPcatTables = 0; EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 *pPathPcdPcatTable = NULL; if (IS_ACPI_HEADER_REV_INVALID(pConfigInput)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Error: Invalid revision value %d for PCD config input table.", pConfigInput->Header.Revision); return; } PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PCD_TABLE_STR, L"Platform Config Data Conf Input table"); PrintPcdTableHeader(&pConfigInput->Header, pPrinterCtx, pPath); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"SequenceNumber ", FORMAT_HEX_NOWIDTH, pConfigInput->SequenceNumber); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"SequenceNumber ", L"\n"); /** Check if there is at least one PCAT table **/ if (pConfigInput->Header.Length <= sizeof(*pConfigInput)) { return; } pCurPcatTable = (PCAT_TABLE_HEADER *)&pConfigInput->pPcatTables; SizeOfPcatTables = pConfigInput->Header.Length - (UINT32)((UINT8 *)pCurPcatTable - (UINT8 *)pConfigInput); /** Example of the use of the while loop condition PCAT table #1 offset: 0 size: 10 PCAT table #2 offset: 10 size: 5 Size of PCAT tables: 15 (10 + 5) Iteration #1: offset: 0 Iteration #2: offset: 10 Iteration #3: offset: 15 stop the loop: offset isn't less than size **/ while ((UINT32)((UINT8 *)pCurPcatTable - (UINT8 *)&pConfigInput->pPcatTables) < SizeOfPcatTables) { if (pCurPcatTable->Type == PCAT_TYPE_PARTITION_SIZE_CHANGE_TABLE) { PRINTER_BUILD_KEY_PATH(pPathPcdPcatTable, DS_PCD_PCAT_TABLE_INDEX_PATH, gDimmIndex, 1, ConfigIndex, 0); pPartSizeChange = (NVDIMM_PARTITION_SIZE_CHANGE *)pCurPcatTable; PrintPcdPartitionSizeChange(pPartSizeChange, pPrinterCtx, pPathPcdPcatTable); pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pPartSizeChange->Header.Length); } else if (pCurPcatTable->Type == PCAT_TYPE_INTERLEAVE_INFORMATION_TABLE) { PRINTER_BUILD_KEY_PATH(pPathPcdPcatTable, DS_PCD_PCAT_TABLE_INDEX_PATH, gDimmIndex, 1, ConfigIndex, 1); PrintPcdInterleaveInformation(pCurPcatTable, pConfigInput->Header.Revision, pPrinterCtx, pPathPcdPcatTable); pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pCurPcatTable->Length); } else if (pCurPcatTable->Type == PCAT_TYPE_CONFIG_MANAGEMENT_ATTRIBUTES_TABLE) { PRINTER_BUILD_KEY_PATH(pPathPcdPcatTable, DS_PCD_PCAT_TABLE_INDEX_PATH, gDimmIndex, 1, ConfigIndex, 2); pConfigManagementAttributesInfo = (CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *)pCurPcatTable; PrintPcdConfigManagementAttributesInformation(pConfigManagementAttributesInfo, pPrinterCtx, pPathPcdPcatTable); pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pConfigManagementAttributesInfo->Header.Length); } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Error: wrong PCAT table type"); break; } FREE_POOL_SAFE(pPathPcdPcatTable); } } /** Print Platform Config Data Config Output table and its PCAT tables @param[in] pConfigOutput Config Output table @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdConfOutput( IN NVDIMM_PLATFORM_CONFIG_OUTPUT *pConfigOutput, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ) { NVDIMM_PARTITION_SIZE_CHANGE *pPartSizeChange = NULL; CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *pConfigManagementAttributesInfo = NULL; PCAT_TABLE_HEADER *pCurPcatTable = NULL; UINT32 SizeOfPcatTables = 0; EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 *pPathPcdPcatTable = NULL; if (IS_ACPI_HEADER_REV_INVALID(pConfigOutput)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Error: Invalid revision value %d for PCD config output table.", pConfigOutput->Header.Revision); return; } PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PCD_TABLE_STR, L"Platform Config Data Conf Output table"); PrintPcdTableHeader(&pConfigOutput->Header, pPrinterCtx, pPath); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"SequenceNumber ", FORMAT_HEX_NOWIDTH, pConfigOutput->SequenceNumber); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"ValidationStatus ", FORMAT_HEX_NOWIDTH, pConfigOutput->ValidationStatus); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"ValidationStatus ", L"\n"); /** Check if there is at least one PCAT table **/ if (pConfigOutput->Header.Length <= sizeof(*pConfigOutput)) { return; } pCurPcatTable = (PCAT_TABLE_HEADER *)&pConfigOutput->pPcatTables; SizeOfPcatTables = pConfigOutput->Header.Length - (UINT32)((UINT8 *)pCurPcatTable - (UINT8 *)pConfigOutput); /** Example of the use of the while loop condition PCAT table #1 offset: 0 size: 10 PCAT table #2 offset: 10 size: 5 Size of PCAT tables: 15 (10 + 5) Iteration #1: offset: 0 Iteration #2: offset: 10 Iteration #3: offset: 15 stop the loop: offset isn't less than size **/ while ((UINT32)((UINT8 *)pCurPcatTable - (UINT8 *)&pConfigOutput->pPcatTables) < SizeOfPcatTables) { if (pCurPcatTable->Type == PCAT_TYPE_PARTITION_SIZE_CHANGE_TABLE) { PRINTER_BUILD_KEY_PATH(pPathPcdPcatTable, DS_PCD_PCAT_TABLE_INDEX_PATH, gDimmIndex, 1, ConfigIndex, 0); pPartSizeChange = (NVDIMM_PARTITION_SIZE_CHANGE *)pCurPcatTable; PrintPcdPartitionSizeChange(pPartSizeChange, pPrinterCtx, pPathPcdPcatTable); pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pPartSizeChange->Header.Length); } else if (pCurPcatTable->Type == PCAT_TYPE_INTERLEAVE_INFORMATION_TABLE) { PRINTER_BUILD_KEY_PATH(pPathPcdPcatTable, DS_PCD_PCAT_TABLE_INDEX_PATH, gDimmIndex, 1, ConfigIndex, 1); PrintPcdInterleaveInformation(pCurPcatTable, pConfigOutput->Header.Revision, pPrinterCtx, pPathPcdPcatTable); pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pCurPcatTable->Length); } else if (pCurPcatTable->Type == PCAT_TYPE_CONFIG_MANAGEMENT_ATTRIBUTES_TABLE) { pConfigManagementAttributesInfo = (CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *)pCurPcatTable; pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pConfigManagementAttributesInfo->Header.Length); } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Error: wrong PCAT table type"); break; } FREE_POOL_SAFE(pPathPcdPcatTable); } } /** Print Platform Config Data Configuration Header table and all subtables @param[in] pConfHeader Configuration Header table @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdConfigurationHeader( IN NVDIMM_CONFIGURATION_HEADER *pConfHeader, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ) { CHAR16 *pPathPcdTable = NULL; ConfigIndex = 0; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Table ", L"PCD Config Header"); PrintPcdTableHeader(&pConfHeader->Header, pPrinterCtx, pPath); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"CurrentConfDataSize ", FORMAT_HEX_NOWIDTH, pConfHeader->CurrentConfDataSize); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"CurrentConfStartOffset ", FORMAT_HEX_NOWIDTH, pConfHeader->CurrentConfStartOffset); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"ConfInputDataSize ", FORMAT_HEX_NOWIDTH, pConfHeader->ConfInputDataSize); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"ConfInputDataOffset ", FORMAT_HEX_NOWIDTH, pConfHeader->ConfInputStartOffset); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"ConfOutputDataSize ", FORMAT_HEX_NOWIDTH, pConfHeader->ConfOutputDataSize); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"ConfOutputDataOffset ", FORMAT_HEX_NOWIDTH, pConfHeader->ConfOutputStartOffset); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"ConfOutputDataOffset ", L"\n"); if (pConfHeader->CurrentConfStartOffset != 0 && pConfHeader->CurrentConfDataSize != 0) { PRINTER_BUILD_KEY_PATH(pPathPcdTable, DS_TABLE_INDEX_PATH, gDimmIndex, 1, ConfigIndex); PrintPcdCurrentConfig(GET_NVDIMM_CURRENT_CONFIG(pConfHeader), pPrinterCtx, pPathPcdTable); ConfigIndex++; } if (pConfHeader->ConfInputStartOffset != 0 && pConfHeader->ConfInputDataSize != 0) { PRINTER_BUILD_KEY_PATH(pPathPcdTable, DS_TABLE_INDEX_PATH, gDimmIndex, 1, ConfigIndex); PrintPcdConfInput(GET_NVDIMM_PLATFORM_CONFIG_INPUT(pConfHeader), pPrinterCtx, pPathPcdTable); ConfigIndex++; } if (pConfHeader->ConfOutputStartOffset != 0 && pConfHeader->ConfOutputDataSize != 0) { PRINTER_BUILD_KEY_PATH(pPathPcdTable, DS_TABLE_INDEX_PATH, gDimmIndex, 1, ConfigIndex); PrintPcdConfOutput(GET_NVDIMM_PLATFORM_CONFIG_OUTPUT(pConfHeader), pPrinterCtx, pPathPcdTable); ConfigIndex++; } FREE_POOL_SAFE(pPathPcdTable); } /** Print Namespace Index @param[in] pNamespaceIndex Namespace Index **/ VOID PrintNamespaceIndex( IN NAMESPACE_INDEX *pNamespaceIndex ) { CHAR16 Buffer[NSINDEX_SIG_LEN + 1]; if (pNamespaceIndex == NULL) { return; } ZeroMem(Buffer, sizeof(Buffer)); AsciiStrToUnicodeStrS((CHAR8 *)&pNamespaceIndex->Signature, Buffer, NSINDEX_SIG_LEN + 1); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, L"Signature ", FORMAT_STR, Buffer); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, L"Flags ", FORMAT_HEX_NOWIDTH, *pNamespaceIndex->Flags); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, L"LabelSize ", FORMAT_HEX_NOWIDTH, pNamespaceIndex->LabelSize); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, L"Sequence ", FORMAT_HEX_NOWIDTH, pNamespaceIndex->Sequence); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, L"MyOffset ", FORMAT_UINT64_HEX_NOWIDTH, pNamespaceIndex->MyOffset); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, L"MySize ", FORMAT_UINT64_HEX_NOWIDTH, pNamespaceIndex->MySize); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, L"OtherOffset ", FORMAT_UINT64_HEX_NOWIDTH, pNamespaceIndex->OtherOffset); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, L"LabelOffset ", FORMAT_UINT64_HEX_NOWIDTH, pNamespaceIndex->LabelOffset); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, L"NumOfLabel ", FORMAT_HEX_NOWIDTH, pNamespaceIndex->NumberOfLabels); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, L"Major ", FORMAT_HEX_NOWIDTH, pNamespaceIndex->Major); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, L"Minor ", FORMAT_HEX_NOWIDTH, pNamespaceIndex->Minor); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, L"Checksum ", FORMAT_UINT64_HEX_NOWIDTH, pNamespaceIndex->Checksum); PrintLsaHex(pNamespaceIndex->pFree, LABELS_TO_FREE_BYTES(ROUNDUP(pNamespaceIndex->NumberOfLabels, 8))); } /** Print Namespace Label @param[in] pNamespaceLabel Namespace Label @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintNamespaceLabel( IN NAMESPACE_LABEL *pNamespaceLabel, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ) { CHAR16 Buffer[NLABEL_NAME_LEN_WITH_TERMINATOR]; CHAR16 *pUuidStr = NULL; CHAR16 *pTypeGuidStr = NULL; CHAR16 *pAddrAbstrGuidStr = NULL; if (pNamespaceLabel == NULL) { return; } ZeroMem(Buffer, sizeof(Buffer)); AsciiStrToUnicodeStrS((CHAR8 *)&pNamespaceLabel->Name, Buffer, NLABEL_NAME_LEN_WITH_TERMINATOR); pUuidStr = GuidToStr(&pNamespaceLabel->Uuid); pTypeGuidStr = GuidToStr(&pNamespaceLabel->TypeGuid); pAddrAbstrGuidStr = GuidToStr(&pNamespaceLabel->AddressAbstractionGuid); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Uuid ", FORMAT_STR, pUuidStr); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Name ", FORMAT_STR, Buffer); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"LabelFlags ", FORMAT_HEX_NOWIDTH, pNamespaceLabel->Flags); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"NumOfLabels ", FORMAT_HEX_NOWIDTH, pNamespaceLabel->NumberOfLabels); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Position ", FORMAT_HEX_NOWIDTH, pNamespaceLabel->Position); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"ISetCookie ", FORMAT_UINT64_HEX_NOWIDTH, pNamespaceLabel->InterleaveSetCookie); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"LbaSize ", FORMAT_UINT64_HEX_NOWIDTH, pNamespaceLabel->LbaSize); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Dpa ", FORMAT_UINT64_HEX_NOWIDTH, pNamespaceLabel->Dpa); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"RawSize ", FORMAT_UINT64_HEX_NOWIDTH, pNamespaceLabel->RawSize); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Slot ", FORMAT_HEX_NOWIDTH, pNamespaceLabel->Slot); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Alignment ", FORMAT_HEX_NOWIDTH, pNamespaceLabel->Alignment); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"TypeGuid ", FORMAT_STR, pTypeGuidStr); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"AddrAbstrGuid ", FORMAT_STR, pAddrAbstrGuidStr); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"LabelChecksum ", FORMAT_UINT64_HEX_NOWIDTH, pNamespaceLabel->Checksum); PRINTER_APPEND_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"LabelChecksum ", L"\n"); FREE_POOL_SAFE(pUuidStr); FREE_POOL_SAFE(pTypeGuidStr); FREE_POOL_SAFE(pAddrAbstrGuidStr); } /** Print Label Storage Area and all subtables @param[in] pLba Label Storage Area @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintLabelStorageArea( IN LABEL_STORAGE_AREA *pLba, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT16 Index = 0; UINT16 CurrentIndex = 0; UINT16 SlotStatus = SLOT_UNKNOWN; BOOLEAN First = FALSE; PRINT_CONTEXT *pPrinterCtxNsLabel = NULL; CHAR16 *pPathNsLabel = NULL; pPrinterCtxNsLabel = pPrinterCtx; if (pLba == NULL) { return; } ReturnCode = GetLsaIndexes(pLba, &CurrentIndex, NULL); if (EFI_ERROR(ReturnCode)) { return; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"Label Storage Area ", L"Current Index"); gpPathLba = pPath; gpPrinterCtxLbaCommon = pPrinterCtx; PrintNamespaceIndex(&pLba->Index[CurrentIndex]); for (Index = 0, First = TRUE; Index < pLba->Index[CurrentIndex].NumberOfLabels; Index++) { CheckSlotStatus(&pLba->Index[CurrentIndex], Index, &SlotStatus); if (SlotStatus == SLOT_FREE) { continue; } if (First) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"Labels", L"Label Storage Area "); First = FALSE; } PRINTER_BUILD_KEY_PATH(pPathNsLabel, DS_TABLE_INDEX_PATH, gDimmIndex, 0, Index); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtxNsLabel, pPathNsLabel, PCD_TABLE_STR, L"Namespace Label Info"); PrintNamespaceLabel(&pLba->pLabels[Index], pPrinterCtxNsLabel, pPathNsLabel); } FREE_POOL_SAFE(pPathNsLabel); } /** Function that allows for nicely formatted HEX & ASCII console output. It can be used to inspect memory objects without a need for debugger or dumping raw DIMM data. @param[in] pBuffer Pointer to an arbitrary object @param[in] Bytes Number of bytes to display **/ VOID PrintLsaHex( IN VOID *pBuffer, IN UINT32 Bytes ) { UINT8 Byte, AsciiBuffer[COLUMN_IN_HEX_DUMP]; UINT16 Column, NextColumn, Index, Index2; UINT8 *pData; CHAR16 *message = NULL; CHAR16 *messageId = NULL; if (pBuffer == NULL) { NVDIMM_DBG("pBuffer is NULL"); return; } PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, L"Hexdump", L"For %d bytes", Bytes); pData = (UINT8 *)pBuffer; for (Index = 0; Index < Bytes; Index++) { Column = Index % COLUMN_IN_HEX_DUMP; NextColumn = (Index + 1) % COLUMN_IN_HEX_DUMP; Byte = *(pData + Index); if (Column == 0) { messageId = CatSPrintClean(messageId, L"%.3d", Index); } if (Index % 8 == 0) { message = CatSPrintClean(message, L" "); } message = CatSPrintClean(message, L"%.2x", *(pData + Index)); AsciiBuffer[Column] = IsAsciiAlnumCharacter(Byte) ? Byte : '.'; if (NextColumn == 0 && Index != 0) { message = CatSPrintClean(message, L" "); for (Index2 = 0; Index2 < COLUMN_IN_HEX_DUMP; Index2++) { message = CatSPrintClean(message, L"%c", AsciiBuffer[Index2]); if (Index2 == COLUMN_IN_HEX_DUMP / 2 - 1) { message = CatSPrintClean(message, L" "); } } PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(gpPrinterCtxLbaCommon, gpPathLba, messageId, message); FREE_POOL_SAFE(message); FREE_POOL_SAFE(messageId); } } FREE_POOL_SAFE(message); FREE_POOL_SAFE(messageId); } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowPcdCommand.h000066400000000000000000000114611440615110200207310ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_PCD_COMMAND_ #define _SHOW_PCD_COMMAND_ /** Register the Show PCD command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowPcdCommand( ); /** Execute the Show PCD command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters **/ EFI_STATUS ShowPcd( IN struct Command *pCmd ); /** Print Platform Config Data table header @param[in] pHeader table header @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdTableHeader( IN TABLE_HEADER *pHeader, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ); /** Print Platform Config Data PCAT table header @param[in] pHeader PCAT table header @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdPcatTableHeader( IN PCAT_TABLE_HEADER *pHeader, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ); /** Print Platform Config Data Partition Size Change table @param[in] pPartitionSizeChange Partition Size Change table @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdPartitionSizeChange( IN NVDIMM_PARTITION_SIZE_CHANGE *pPartitionSizeChange, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ); /** Print Platform Config Data Identification Information table @param[in] pIdentificationInfo Identification Information table @param[in] PcdConfigTableRevision Revision of the PCD Config tables @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdIdentificationInformation( IN VOID *pIdentificationInfo, IN ACPI_REVISION PcdConfigTableRevision, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ); /** Print Platform Config Data Interleave Information table and its extension tables @param[in] pInterleaveInfo Interleave Information table @param[in] PcdConfigTableRevision Revision of the PCD Config tables @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdInterleaveInformation( IN PCAT_TABLE_HEADER *pInterleaveInfo, IN ACPI_REVISION PcdConfigTableRevision, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ); /** Print Platform Config Data Current Config table and its PCAT tables @param[in] pCurrentConfig Current Config table @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdCurrentConfig( IN NVDIMM_CURRENT_CONFIG *pCurrentConfig, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ); /** Print Platform Config Data Config Input table and its PCAT tables @param[in] pConfigInput Config Input table @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdConfInput( IN NVDIMM_PLATFORM_CONFIG_INPUT *pConfigInput, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ); /** Print Platform Config Data Config Output table and its PCAT tables @param[in] pConfigOutput Config Output table @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdConfOutput( IN NVDIMM_PLATFORM_CONFIG_OUTPUT *pConfigOutput, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ); /** Print Platform Config Data Configuration Header table and all subtables @param[in] pConfHeader Configuration Header table @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintPcdConfigurationHeader( IN NVDIMM_CONFIGURATION_HEADER *pConfHeader, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ); /** Print Namespace Index @param[in] pNamespaceIndex Namespace Index **/ VOID PrintNamespaceIndex( IN NAMESPACE_INDEX *pNamespaceIndex ); /** Print Namespace Label @param[in] pNamespaceLabel Namespace Label @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintNamespaceLabel( IN NAMESPACE_LABEL *pNamespaceLabel, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ); /** Print Label Storage Area and all subtables @param[in] pLba Label Storage Area @param[in] pPrinterCtx pointer for printer @param[in] pPath **/ VOID PrintLabelStorageArea( IN LABEL_STORAGE_AREA *pLba, IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath ); /** Function that allows for nicely formatted HEX & ASCII console output. It can be used to inspect memory objects without a need for debugger or dumping raw DIMM data. @param[in] pBuffer Pointer to an arbitrary object @param[in] Bytes Number of bytes to display **/ VOID PrintLsaHex( IN VOID *pBuffer, IN UINT32 Bytes ); #endif /* _SHOW_PCD_COMMAND_ */ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowPerformanceCommand.c000066400000000000000000000306631440615110200224640ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include "CommandParser.h" #include "ShowPerformanceCommand.h" #include "Common.h" #include "Convert.h" #include "NvmTypes.h" #define DS_ROOT_PATH L"/DimmPerformanceList" #define DS_SOCKET_PATH L"/DimmPerformanceList/DimmPerformance" #define DS_SOCKET_INDEX_PATH L"/DimmPerformanceList/DimmPerformance[%d]" /* * PRINT LIST ATTRIBUTES * ---DimmId=0x0001--- * MediaReads=0x000000000000000000000000cc3bb004 * MediaWrites=0x00000000000000000000000049437ab4 * ReadRequests=0x000000000000000000000000000c0008 * ... */ PRINTER_LIST_ATTRIB ShowPerformanceListAttributes = { { { L"DimmPerformance", //GROUP LEVEL TYPE L"---" DIMM_ID_STR L"=$(" DIMM_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR DIMM_ID_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; PRINTER_DATA_SET_ATTRIBS ShowPerformanceDataSetAttribs = { &ShowPerformanceListAttributes, NULL }; EFI_STATUS ShowPerformance(IN struct Command *pCmd); /** Command syntax definition **/ struct Command ShowPerformanceCommand = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"", HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #else {L"", L"", L"", L"", L"",FALSE, ValueOptional} #endif }, { //!< targets { DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional }, { PERFORMANCE_TARGET, L"", HELP_TEXT_PERFORMANCE_CAT, TRUE, ValueOptional } }, { //!< properties { L"", L"", L"", FALSE, ValueOptional }, }, L"Show performance statistics of one or more " PMEM_MODULES_STR L".", //!< help ShowPerformance, TRUE, //!< enable print control support }; CHAR16 *mppAllowedShowPerformanceDisplayValues[] = { DCPMM_PERFORMANCE_MEDIA_READS, DCPMM_PERFORMANCE_MEDIA_WRITES, DCPMM_PERFORMANCE_READ_REQUESTS, DCPMM_PERFORMANCE_WRITE_REQUESTS, DCPMM_PERFORMANCE_TOTAL_MEDIA_READS, DCPMM_PERFORMANCE_TOTAL_MEDIA_WRITES, DCPMM_PERFORMANCE_TOTAL_READ_REQUESTS, DCPMM_PERFORMANCE_TOTAL_WRITE_REQUESTS }; #define PERFORMANCE_DATA_FORMAT L"0x"FORMAT_UINT64_HEX FORMAT_UINT64_HEX EFI_STATUS GetDimmIdOrDimmHandleToPrint(UINT16 DimmId, DIMM_INFO *AllDimmInfos, UINT32 DimmCount, UINT32 *HandleToPrint) { UINT32 Index = 0; for (Index = 0; Index < DimmCount; ++Index) { if (AllDimmInfos[Index].DimmID == DimmId) { *HandleToPrint = AllDimmInfos[Index].DimmHandle; return EFI_SUCCESS; } } return EFI_INVALID_PARAMETER; } STATIC VOID PrintPerformanceData(PRINT_CONTEXT *pPrinterCtx, UINT16 *DimmId, UINT32 DimmIdsNum, DIMM_INFO *AllDimmInfos, UINT32 DimmCount, DIMM_PERFORMANCE_DATA *pDimmsPerformanceData, BOOLEAN AllOptionSet, BOOLEAN DisplayOptionSet, CHAR16 *pDisplayOptionValue) { UINT32 AllDimmsIndex = 0; UINT32 InfoIndex = 0; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; UINT32 DimmIndex = 0; CHAR16 *pPath = NULL; BOOLEAN InfoFound = FALSE; // Account for multiple or no input dimms given for (AllDimmsIndex = 0; AllDimmsIndex < DimmCount; AllDimmsIndex++) { if (DimmIdsNum > 0 && !ContainUint(DimmId, DimmIdsNum, pDimmsPerformanceData[AllDimmsIndex].DimmId)) { continue; } // find info record that corresponds to the performance data InfoFound = FALSE; for (InfoIndex = 0; InfoIndex < DimmCount; InfoIndex++) { if (AllDimmInfos[InfoIndex].DimmID == pDimmsPerformanceData[AllDimmsIndex].DimmId) { InfoFound = TRUE; break; } } // skip record with no matching info record if (!InfoFound) { continue; } // Print the DimmID ReturnCode = GetPreferredDimmIdAsString(AllDimmInfos[InfoIndex].DimmHandle, AllDimmInfos[InfoIndex].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { continue; } PRINTER_BUILD_KEY_PATH(pPath, DS_SOCKET_INDEX_PATH, DimmIndex); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, DimmStr); /** MediaReads **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayOptionValue, DCPMM_PERFORMANCE_MEDIA_READS))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DCPMM_PERFORMANCE_MEDIA_READS, PERFORMANCE_DATA_FORMAT, pDimmsPerformanceData[AllDimmsIndex].MediaReads.Uint64_1, pDimmsPerformanceData[AllDimmsIndex].MediaReads.Uint64); } /** MediaWrites **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayOptionValue, DCPMM_PERFORMANCE_MEDIA_WRITES))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DCPMM_PERFORMANCE_MEDIA_WRITES, PERFORMANCE_DATA_FORMAT, pDimmsPerformanceData[AllDimmsIndex].MediaWrites.Uint64_1, pDimmsPerformanceData[AllDimmsIndex].MediaWrites.Uint64); } /** ReadRequests **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayOptionValue, DCPMM_PERFORMANCE_READ_REQUESTS))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DCPMM_PERFORMANCE_READ_REQUESTS, PERFORMANCE_DATA_FORMAT, pDimmsPerformanceData[AllDimmsIndex].ReadRequests.Uint64_1, pDimmsPerformanceData[AllDimmsIndex].ReadRequests.Uint64); } /** WriteRequests **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayOptionValue, DCPMM_PERFORMANCE_WRITE_REQUESTS))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DCPMM_PERFORMANCE_WRITE_REQUESTS, PERFORMANCE_DATA_FORMAT, pDimmsPerformanceData[AllDimmsIndex].WriteRequests.Uint64_1, pDimmsPerformanceData[AllDimmsIndex].WriteRequests.Uint64); } /** TotalMediaReads **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayOptionValue, DCPMM_PERFORMANCE_TOTAL_MEDIA_READS))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DCPMM_PERFORMANCE_TOTAL_MEDIA_READS, PERFORMANCE_DATA_FORMAT, pDimmsPerformanceData[AllDimmsIndex].TotalMediaReads.Uint64_1, pDimmsPerformanceData[AllDimmsIndex].TotalMediaReads.Uint64); } /** TotalMediaWrites **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayOptionValue, DCPMM_PERFORMANCE_TOTAL_MEDIA_WRITES))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DCPMM_PERFORMANCE_TOTAL_MEDIA_WRITES, PERFORMANCE_DATA_FORMAT, pDimmsPerformanceData[AllDimmsIndex].TotalMediaWrites.Uint64_1, pDimmsPerformanceData[AllDimmsIndex].TotalMediaWrites.Uint64); } /** TotalReadRequests **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayOptionValue, DCPMM_PERFORMANCE_TOTAL_READ_REQUESTS))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DCPMM_PERFORMANCE_TOTAL_READ_REQUESTS, PERFORMANCE_DATA_FORMAT, pDimmsPerformanceData[AllDimmsIndex].TotalReadRequests.Uint64_1, pDimmsPerformanceData[AllDimmsIndex].TotalReadRequests.Uint64); } /** TotalWriteRequests **/ if (AllOptionSet || (DisplayOptionSet && ContainsValue(pDisplayOptionValue, DCPMM_PERFORMANCE_TOTAL_WRITE_REQUESTS))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DCPMM_PERFORMANCE_TOTAL_WRITE_REQUESTS, PERFORMANCE_DATA_FORMAT, pDimmsPerformanceData[AllDimmsIndex].TotalWriteRequests.Uint64_1, pDimmsPerformanceData[AllDimmsIndex].TotalWriteRequests.Uint64); } ++DimmIndex; } FREE_POOL_SAFE(pPath); } /** Execute the Show Performance command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS ShowPerformance( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; UINT32 DimmCount; DIMM_PERFORMANCE_DATA *pDimmsPerformanceData = NULL; UINT32 DimmIdsNum = 0; CHAR16 *pDimmsValue = NULL; UINT32 DimmsCount = 0; DIMM_INFO *pDimms = NULL; UINT16 *pDimmIds = NULL; // Print all unless some are specified BOOLEAN AllOptionSet = TRUE; BOOLEAN DisplayOptionSet = FALSE; CHAR16 *pPerformanceValueStr = NULL; UINT16 Index; PRINT_CONTEXT *pPrinterCtx = NULL; if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; // Make sure we can access the config protocol ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmsCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } if (ContainTarget(pCmd, DIMM_TARGET)) { pDimmsValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pDimmsValue, pDimms, DimmsCount, &pDimmIds, &DimmIdsNum); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Target value is not a valid Dimm ID"); goto Finish; } if (!AllDimmsInListAreManageable(pDimms, DimmsCount, pDimmIds, DimmIdsNum)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } } // So, instead of parsing the -d parameter with CheckAllAndDisplayOptions(), // we're going to maintain the existing implementation for now of just taking // a comma separated list after the performance parameter // Get any specified parameters after "-performance" // TODO: Decide what we want to do long term and move to Common.c pPerformanceValueStr = GetTargetValue(pCmd, PERFORMANCE_TARGET); // Check if any valid strings exist in the performance value string // TODO: Add invalid value checking for (Index = 0; Index < ALLOWED_DISP_VALUES_COUNT(mppAllowedShowPerformanceDisplayValues); Index++) { if (ContainsValue(pPerformanceValueStr, mppAllowedShowPerformanceDisplayValues[Index])) { AllOptionSet = FALSE; DisplayOptionSet = TRUE; break; // If we find a match, leave the loop } } // Get the performance data ReturnCode = pNvmDimmConfigProtocol->GetDimmsPerformanceData(pNvmDimmConfigProtocol, &DimmCount, &pDimmsPerformanceData); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Print the data out PrintPerformanceData(pPrinterCtx, pDimmIds, DimmIdsNum, pDimms, DimmCount, pDimmsPerformanceData, AllOptionSet, DisplayOptionSet, pPerformanceValueStr); //Specify table attributes PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowPerformanceDataSetAttribs); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimmsPerformanceData); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /* * Register the show dimms command */ EFI_STATUS RegisterShowPerformanceCommand( ) { EFI_STATUS Rc = EFI_SUCCESS; NVDIMM_ENTRY(); Rc = RegisterCommand(&ShowPerformanceCommand); NVDIMM_EXIT_I64(Rc); return Rc; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowPerformanceCommand.h000066400000000000000000000011531440615110200224610ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_PERFORMANCE_COMMAND_H_ #define _SHOW_PERFORMANCE_COMMAND_H_ #include /** Execute the Show Performance command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS ShowPerformance( IN struct Command *pCmd ); /* * Register the show dimms command */ EFI_STATUS RegisterShowPerformanceCommand( ); #endif //_SHOW_PERFORMANCE_COMMAND_H_ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowPreferencesCommand.c000066400000000000000000000133561440615110200224640ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "Debug.h" #include "Types.h" #include "Utility.h" #include "NvmInterface.h" #include "CommandParser.h" #include "ShowPreferencesCommand.h" #include "Common.h" #include "Convert.h" #include #ifdef OS_BUILD #include #endif #define DS_ROOT_PATH L"/Preferences" /** Command syntax definition **/ struct Command ShowPreferencesCommand = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"", HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #else {L"", L"", L"", L"",L"", FALSE, ValueOptional} #endif }, { //!< targets {PREFERENCES_TARGET, L"", L"", TRUE, ValueEmpty}, }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show user preferences.", //!< help ShowPreferences, TRUE }; /** Execute the Show Preferences command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action @retval EFI_DEVICE_ERROR Communications failure with driver @retval EFI_NOT_FOUND Cli display preferences could not be retrieved successfully **/ EFI_STATUS ShowPreferences( IN struct Command *pCmd ) { EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; COMMAND_STATUS *pCommandStatus = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; DRIVER_PREFERENCES DriverPreferences; DISPLAY_PREFERENCES DisplayPreferences; CONST CHAR16 *pImcInterleaving = NULL; CONST CHAR16 *pChannelInterleaving = NULL; #ifdef OS_BUILD CHAR16 tempStr[PROPERTY_VALUE_LEN]; UINTN TempStrLen = PROPERTY_VALUE_LEN; #endif PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; NVDIMM_ENTRY(); ZeroMem(&DriverPreferences, sizeof(DriverPreferences)); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } /** Need NvmDimmConfigProtocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetDriverPreferences(pNvmDimmConfigProtocol, &DriverPreferences, pCommandStatus); if (EFI_ERROR(ReturnCode)) { ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_COMMAND_STATUS(pCmd->pPrintCtx, ReturnCode, L"Show preferences", L" on", pCommandStatus); goto Finish; } ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } PRINTER_BUILD_KEY_PATH(pPath, DS_ROOT_PATH); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, CLI_DEFAULT_DIMM_ID_PROPERTY, GetDimmIDStr(DisplayPreferences.DimmIdentifier)); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, CLI_DEFAULT_SIZE_PROPERTY, GetDisplaySizeStr(DisplayPreferences.SizeUnit)); if (DriverPreferences.ImcInterleaving == DEFAULT_IMC_INTERLEAVE_SIZE && DriverPreferences.ChannelInterleaving == DEFAULT_CHANNEL_INTERLEAVE_SIZE) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, APP_DIRECT_SETTINGS_PROPERTY, PROPERTY_VALUE_RECOMMENDED); } else { pChannelInterleaving = ParseChannelInterleavingValue(DriverPreferences.ChannelInterleaving); pImcInterleaving = ParseImcInterleavingValue(DriverPreferences.ImcInterleaving); if (pChannelInterleaving == NULL || pImcInterleaving == NULL) { ReturnCode = EFI_DEVICE_ERROR; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, APP_DIRECT_SETTINGS_PROPERTY, FORMAT_STR L"_" FORMAT_STR_NL, pImcInterleaving, pChannelInterleaving); } } #ifdef OS_BUILD ReturnCode = GET_VARIABLE_STR(DBG_LOG_LEVEL, gNvmDimmConfigProtocolGuid, &TempStrLen, tempStr); if (!EFI_ERROR(ReturnCode)) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DBG_LOG_LEVEL, tempStr); } #endif Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pPath); FreeCommandStatus(&pCommandStatus); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the Show Preferences command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowPreferencesCommand( ) { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowPreferencesCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowPreferencesCommand.h000066400000000000000000000014331440615110200224620ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_PREFERENCES_COMMAND_ #define _SHOW_PREFERENCES_COMMAND_ #include #include "CommandParser.h" /** Register the Show Preferences command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowPreferencesCommand(); /** Execute the Show Preferences command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS ShowPreferences( IN struct Command *pCmd ); #endif /** _SHOW_PREFERENCES_COMMAND_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowRegionsCommand.c000066400000000000000000000512261440615110200216270ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include "ShowRegionsCommand.h" #include #include #include #include #include #include "Common.h" #include "NvmDimmCli.h" #include #ifdef OS_BUILD #include "BaseMemoryLib.h" #else #include #endif #define DS_ROOT_PATH L"/RegionList" #define DS_REGION_PATH L"/RegionList/Region" #define DS_DIMM_INDEX_PATH L"/RegionList/Region[%d]" #ifdef OS_BUILD /* * PRINT LIST ATTRIBUTES * ---ISetID=0xce8049e0a393f6ea--- * SocketID=0x00000000 * PersistentMemoryType=AppDirect * Capacity=750.0 GiB * FreeCapacity=750.0 GiB * HealthState=Locked * DimmID=0x0001, 0x0011, 0x0021, 0x0101, 0x0111, 0x0121 * ... */ PRINTER_LIST_ATTRIB ShowRegionListAttributes = { { { REGION_NODE_STR, //GROUP LEVEL TYPE L"---" ISET_ID_STR L"=$(" ISET_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR ISET_ID_STR L";" REGION_ID_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; #else /* * PRINT LIST ATTRIBUTES * ---IRegionID=0x0001--- * SocketID=0x00000000 * PersistentMemoryType=AppDirect * Capacity=750.0 GiB * FreeCapacity=750.0 GiB * HealthState=Locked * DimmID=0x0001, 0x0011, 0x0021, 0x0101, 0x0111, 0x0121 * ISetID=0xce8049e0a393f6ea * ... */ PRINTER_LIST_ATTRIB ShowRegionListAttributes = { { { REGION_NODE_STR, //GROUP LEVEL TYPE L"---" REGION_ID_STR L"=$(" REGION_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR REGION_ID_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; #endif #ifdef OS_BUILD /* * PRINTER TABLE ATTRIBUTES (6 columns) * SocketID | ISetID | PMEM Type | Capacity | Free Capacity | HealthState * ====================================================================== * 0x0001 | X | X | X | X | X * ... */ PRINTER_TABLE_ATTRIB ShowRegionTableAttributes = { { { SOCKET_ID_STR, //COLUMN HEADER SOCKET_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_REGION_PATH PATH_KEY_DELIM SOCKET_ID_STR //COLUMN DATA PATH }, #ifdef OS_BUILD { ISET_ID_STR, //COLUMN HEADER ISET_ID_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_REGION_PATH PATH_KEY_DELIM ISET_ID_STR //COLUMN DATA PATH }, #else { REGION_ID_STR, //COLUMN HEADER REGION_ID_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_REGION_PATH PATH_KEY_DELIM REGION_ID_STR //COLUMN DATA PATH }, #endif { PERSISTENT_MEM_TYPE_STR, //COLUMN HEADER PMEM_TYPE_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_REGION_PATH PATH_KEY_DELIM PERSISTENT_MEM_TYPE_STR //COLUMN DATA PATH }, { TOTAL_CAPACITY_STR, //COLUMN HEADER CAPACITY_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_REGION_PATH PATH_KEY_DELIM TOTAL_CAPACITY_STR //COLUMN DATA PATH }, { FREE_CAPACITY_STR, //COLUMN HEADER FREE_CAPACITY_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_REGION_PATH PATH_KEY_DELIM FREE_CAPACITY_STR //COLUMN DATA PATH }, { REGION_HEALTH_STATE_STR, //COLUMN HEADER HEALTH_SHORT_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_REGION_PATH PATH_KEY_DELIM REGION_HEALTH_STATE_STR //COLUMN DATA PATH } } }; #else /* * PRINTER TABLE ATTRIBUTES ( columns) * RegionID | SocketID | PMEM Type | Capacity | Free Capacity | HealthState * ================================================================================= * 0x0001 | 0x0001 | X | X | X | X * ... */ PRINTER_TABLE_ATTRIB ShowRegionTableAttributes = { { { REGION_ID_STR, //COLUMN HEADER REGION_ID_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_REGION_PATH PATH_KEY_DELIM REGION_ID_STR //COLUMN DATA PATH }, { SOCKET_ID_STR, //COLUMN HEADER SOCKET_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_REGION_PATH PATH_KEY_DELIM SOCKET_ID_STR //COLUMN DATA PATH }, { PERSISTENT_MEM_TYPE_STR, //COLUMN HEADER PMEM_TYPE_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_REGION_PATH PATH_KEY_DELIM PERSISTENT_MEM_TYPE_STR //COLUMN DATA PATH }, { TOTAL_CAPACITY_STR, //COLUMN HEADER CAPACITY_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_REGION_PATH PATH_KEY_DELIM TOTAL_CAPACITY_STR //COLUMN DATA PATH }, { FREE_CAPACITY_STR, //COLUMN HEADER FREE_CAPACITY_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_REGION_PATH PATH_KEY_DELIM FREE_CAPACITY_STR //COLUMN DATA PATH }, { REGION_HEALTH_STATE_STR, //COLUMN HEADER HEALTH_SHORT_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_REGION_PATH PATH_KEY_DELIM REGION_HEALTH_STATE_STR //COLUMN DATA PATH } } }; #endif PRINTER_DATA_SET_ATTRIBS ShowRegionsDataSetAttribs = { &ShowRegionListAttributes, &ShowRegionTableAttributes }; /** Command syntax definition **/ struct Command ShowRegionsCommand = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"", HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", NFIT_OPTION, L"", L"",HELP_NFIT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", LARGE_PAYLOAD_OPTION, L"", L"", HELP_LPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", SMALL_PAYLOAD_OPTION, L"", L"", HELP_SPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, {ALL_OPTION_SHORT, ALL_OPTION, L"", L"",HELP_ALL_DETAILS_TEXT, FALSE, ValueEmpty}, {DISPLAY_OPTION_SHORT, DISPLAY_OPTION, L"", HELP_TEXT_ATTRIBUTES, HELP_DISPLAY_DETAILS_TEXT, FALSE, ValueRequired}, {UNITS_OPTION_SHORT, UNITS_OPTION, L"", UNITS_OPTION_HELP, HELP_UNIT_DETAILS_TEXT, FALSE, ValueRequired} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif // OS_BUILD }, { //!< targets #ifdef OS_BUILD {REGION_TARGET, L"", L"", TRUE, ValueEmpty}, #endif #ifndef OS_BUILD {REGION_TARGET, L"", HELP_TEXT_REGION_IDS, TRUE, ValueOptional}, #endif { SOCKET_TARGET, L"", HELP_TEXT_SOCKET_IDS, FALSE, ValueOptional }, }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show information about one or more regions.", //!< help ShowRegions, //!< run function TRUE, //!< enable print control support }; CHAR16 *mppAllowedShowRegionsDisplayValues[] = { REGION_ID_STR, PERSISTENT_MEM_TYPE_STR, TOTAL_CAPACITY_STR, FREE_CAPACITY_STR, SOCKET_ID_STR, REGION_HEALTH_STATE_STR, DIMM_ID_STR, ISET_ID_STR, }; /** Register the show regions command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowRegionsCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowRegionsCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Convert the health state to a health string @param[in] Health - Region Health State @retval Const Pointer to Region Health State string **/ STATIC CONST CHAR16 * RegionHealthToString( IN UINT16 Health ) { switch (Health) { case RegionHealthStateNormal: return HEALTHY_STATE; case RegionHealthStateError: return ERROR_STATE; case RegionHealthStatePending: return PENDING_STATE; case RegionHealthStateLocked: return LOCKED_STATE; case RegionHealthStateUnknown: default: return UNKNOWN_STATE; } } /** Execute the show regions command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure @retval EFI_NO_RESPONSE FW busy for one or more dimms **/ EFI_STATUS ShowRegions( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; UINT32 RegionCount = 0; REGION_INFO *pRegions = NULL; UINT16 *pRegionsIds = NULL; UINT32 RegionIdsNum = 0; UINT16 *pSocketIds = NULL; UINT32 SocketsNum = 0; CHAR16 *pSocketsValue = NULL; CHAR16 *pRegionsValue = NULL; BOOLEAN AllOptionSet = FALSE; UINT32 RegionIndex = 0; BOOLEAN Found = FALSE; CHAR16 *pRegionTempStr = NULL; INTERLEAVE_FORMAT *pInterleaveFormat = NULL; UINT16 UnitsOption = DISPLAY_SIZE_UNIT_UNKNOWN; UINT16 UnitsToDisplay = FixedPcdGet16(PcdDcpmmCliDefaultCapacityUnit); CHAR16 *pCapacityStr = NULL; CONST CHAR16 *pHealthStateStr = NULL; DISPLAY_PREFERENCES DisplayPreferences; COMMAND_STATUS *pCommandStatus = NULL; UINT32 AppDirectRegionCount = 0; CMD_DISPLAY_OPTIONS *pDispOptions = NULL; CHAR16 *pDimmIds = NULL; CHAR16 *pNfitOption = NULL; BOOLEAN UseNfit = FALSE; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; NVDIMM_ENTRY(); ReturnCode = EFI_SUCCESS; if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; pDispOptions = AllocateZeroPool(sizeof(CMD_DISPLAY_OPTIONS)); if (NULL == pDispOptions) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = CheckAllAndDisplayOptions(pCmd, mppAllowedShowRegionsDisplayValues, ALLOWED_DISP_VALUES_COUNT(mppAllowedShowRegionsDisplayValues), pDispOptions); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CheckAllAndDisplayOptions has returned error. Code " FORMAT_EFI_STATUS "\n", ReturnCode); goto Finish; } AllOptionSet = (!pDispOptions->AllOptionSet && !pDispOptions->DisplayOptionSet) || pDispOptions->AllOptionSet; #ifdef OS_BUILD ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); #endif /** initialize status structure **/ ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } /** Make sure we can access the config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); ReturnCode = EFI_NOT_FOUND; goto Finish; } /** If sockets were specified **/ if (ContainTarget(pCmd, SOCKET_TARGET)) { pSocketsValue = GetTargetValue(pCmd, SOCKET_TARGET); ReturnCode = GetUintsFromString(pSocketsValue, &pSocketIds, &SocketsNum); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_TARGET_SOCKET); goto Finish; } } /** if Region IDs were passed in, read them **/ if (NULL != pCmd->targets[0].pTargetValueStr && StrLen(pCmd->targets[0].pTargetValueStr) > 0) { pRegionsValue = GetTargetValue(pCmd, REGION_TARGET); ReturnCode = GetUintsFromString(pRegionsValue, &pRegionsIds, &RegionIdsNum); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_TARGET_REGION); goto Finish; } } ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } UnitsToDisplay = DisplayPreferences.SizeUnit; ReturnCode = GetUnitsOption(pCmd, &UnitsOption); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Any valid units option will override the preferences **/ if (UnitsOption != DISPLAY_SIZE_UNIT_UNKNOWN) { UnitsToDisplay = UnitsOption; } /** Check if nfit option is set **/ pNfitOption = getOptionValue(pCmd, NFIT_OPTION); if (pNfitOption) { UseNfit = TRUE; } ReturnCode = pNvmDimmConfigProtocol->GetRegionCount(pNvmDimmConfigProtocol, UseNfit, &RegionCount); if (EFI_ERROR(ReturnCode)) { if (EFI_NO_RESPONSE == ReturnCode) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); } ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, L"Show region", L" on", pCommandStatus); goto Finish; } if (0 == RegionCount) { ReturnCode = EFI_SUCCESS; //WA, to ensure ESX prints a message when no entries are found. if (PRINTER_ESX_FORMAT_ENABLED(pPrinterCtx)) { PRINTER_SET_MSG(pPrinterCtx, EFI_NOT_FOUND, CLI_INFO_NO_REGIONS); } else { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_INFO_NO_REGIONS); } goto Finish; } pRegions = AllocateZeroPool(sizeof(REGION_INFO) * RegionCount); if (pRegions == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetRegions(pNvmDimmConfigProtocol, RegionCount, UseNfit, pRegions, pCommandStatus); if (EFI_ERROR(ReturnCode)) { if (pCommandStatus->GeneralStatus != NVM_SUCCESS) { ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, CLI_INFO_SHOW_REGION, L"", pCommandStatus); } else { ReturnCode = EFI_ABORTED; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); } NVDIMM_WARN("Failed to retrieve the REGION list"); goto Finish; } for (RegionIndex = 0; RegionIndex < RegionCount; RegionIndex++) { if (((pRegions[RegionIndex].RegionType & PM_TYPE_AD) != 0) || ((pRegions[RegionIndex].RegionType & PM_TYPE_AD_NI) != 0)) { AppDirectRegionCount++; } } if (AppDirectRegionCount == 0) { ReturnCode = EFI_SUCCESS; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_INFO_NO_REGIONS); goto Finish; } for (RegionIndex = 0; RegionIndex < RegionCount; RegionIndex++) { /** Skip if the RegionId is not matching. **/ if (RegionIdsNum > 0 && !ContainUint(pRegionsIds, RegionIdsNum, pRegions[RegionIndex].RegionId)) { continue; } /** Skip if the socket is not matching. **/ if (SocketsNum > 0 && !ContainUint(pSocketIds, SocketsNum, pRegions[RegionIndex].SocketId)) { continue; } PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_INDEX_PATH, RegionIndex); Found = TRUE; /** SocketId **/ if (AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SOCKET_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SOCKET_ID_STR, FORMAT_HEX, pRegions[RegionIndex].SocketId); } /** Display all the persistent memory types supported by the region. **/ if (AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, PERSISTENT_MEM_TYPE_STR))) { pRegionTempStr = RegionTypeToString(pRegions[RegionIndex].RegionType); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, PERSISTENT_MEM_TYPE_STR, pRegionTempStr); FREE_POOL_SAFE(pRegionTempStr); } /** Capacity **/ if (AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, TOTAL_CAPACITY_STR))) { ReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pRegions[RegionIndex].Capacity, UnitsToDisplay, TRUE, &pCapacityStr); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_CAPACITY_STRING); goto Finish; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, TOTAL_CAPACITY_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); } /** FreeCapacity **/ if (AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, FREE_CAPACITY_STR))) { ReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pRegions[RegionIndex].FreeCapacity, UnitsToDisplay, TRUE, &pCapacityStr); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_CAPACITY_STRING); goto Finish; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, FREE_CAPACITY_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); } /** HealthState **/ if (AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, REGION_HEALTH_STATE_STR))) { pHealthStateStr = RegionHealthToString(pRegions[RegionIndex].Health); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, REGION_HEALTH_STATE_STR, pHealthStateStr); } /** Dimms **/ if (AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, DIMM_ID_STR))) { ReturnCode = ConvertRegionDimmIdsToDimmListStr(&pRegions[RegionIndex], pNvmDimmConfigProtocol, DisplayPreferences.DimmIdentifier, &pDimmIds); if (EFI_ERROR(ReturnCode)) { goto Finish; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, pDimmIds); FREE_POOL_SAFE(pDimmIds); } /** RegionID **/ #ifdef OS_BUILD if (AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, REGION_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, REGION_ID_STR, FORMAT_HEX, pRegions[RegionIndex].RegionId); } #else /** Always include for non OS builds **/ PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, REGION_ID_STR, FORMAT_HEX, pRegions[RegionIndex].RegionId); #endif /** ISetID **/ #ifdef OS_BUILD /** Always include for OS builds **/ PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, ISET_ID_STR, FORMAT_SHOW_ISET_ID, pRegions[RegionIndex].CookieId); #else if (AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ISET_ID_STR))) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, ISET_ID_STR, FORMAT_SHOW_ISET_ID, pRegions[RegionIndex].CookieId); } #endif } if (RegionIdsNum > 0 && !Found) { CHAR16 *ErrMsg = NULL; ReturnCode = EFI_NOT_FOUND; ErrMsg = CatSPrint(NULL, FORMAT_STR_SPACE FORMAT_STR_NL, CLI_ERR_INVALID_REGION_ID, pCmd->targets[0].pTargetValueStr); if (SocketsNum > 0) { ErrMsg = CatSPrintClean(ErrMsg, CLI_ERR_REGION_TO_SOCKET_MAPPING); } PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR, ErrMsg); FREE_POOL_SAFE(ErrMsg); goto Finish; } //Specify table attributes PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowRegionsDataSetAttribs); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); if (pRegions != NULL) { for (RegionIndex = 0; RegionIndex < RegionCount; RegionIndex++) { pInterleaveFormat = (INTERLEAVE_FORMAT *) pRegions[RegionIndex].PtrInterleaveFormats; FREE_POOL_SAFE(pInterleaveFormat); } } FREE_CMD_DISPLAY_OPTIONS_SAFE(pDispOptions); FREE_POOL_SAFE(pPath); FREE_POOL_SAFE(pRegions); FREE_POOL_SAFE(pRegionsIds); FREE_POOL_SAFE(pSocketIds); FREE_POOL_SAFE(pNfitOption); FreeCommandStatus(&pCommandStatus); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowRegionsCommand.h000066400000000000000000000027031440615110200216300ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_REGIONS_COMMAND_H_ #define _SHOW_REGIONS_COMMAND_H_ #include "CommandParser.h" #define REGION_ID_STR L"RegionID" #define ISET_ID_STR L"ISetID" #define PERSISTENT_MEM_TYPE_STR L"PersistentMemoryType" #define TOTAL_CAPACITY_STR L"Capacity" #define FREE_CAPACITY_STR L"FreeCapacity" #define REGION_HEALTH_STATE_STR L"HealthState" /** Region Health States */ #define HEALTHY_STATE L"Healthy" #define ERROR_STATE L"Error" #define PENDING_STATE L"Pending" #define LOCKED_STATE L"Locked" #define UNKNOWN_STATE L"Unknown" #define DIMM_ID_STR_DELIM L", " /** Register the show regions command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowRegionsCommand( ); /** Execute the show regions command @param[in] pCmd command from CLI @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS ShowRegions( IN struct Command *pCmd ); #endif /* _SHOW_REGIONS_COMMAND_H_ */ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowRegisterCommand.c000066400000000000000000000326131440615110200220040ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include "ShowRegisterCommand.h" #include #include #include #include #include #include #include "Common.h" #define DIMM_ID_STR L"DimmID" #define REGISTER_TARGET_STR L"RegType" #define DS_ROOT_PATH L"/RegList" #define DS_DIMM_PATH L"/RegList/Dimm" #define DS_DIMM_INDEX_PATH L"/RegList/Dimm[%d]" #define DS_REGISTER_PATH L"/RegList/Dimm/Register" #define DS_REGISTER_INDEX_PATH L"/RegList/Dimm[%d]/Register[%d]" /* * PRINT LIST ATTRIBUTES * ---DimmID=0x0011 Registers--- * ---RegType=BSR * Boot Status: 00000000181D00F0 * [07:00] MajorCheckpoint ------: 0xF0 * [15:08] MinorCheckpoint ------: 0x0 * ... */ PRINTER_LIST_ATTRIB ShowRegisterListAttributes = { { { DIMM_NODE_STR, //GROUP LEVEL TYPE L"---" DIMM_ID_STR L"=$(" DIMM_ID_STR L") Registers---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT FORMAT_STR L"=" FORMAT_STR, //NULL or KEY VAL FORMAT STR DIMM_ID_STR //NULL or IGNORE KEY LIST (K1;K2) }, { REGISTER_MODE_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT L"---" REGISTER_TARGET_STR L"=$(" REGISTER_TARGET_STR L")", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR L": " FORMAT_STR, //NULL or KEY VAL FORMAT STR REGISTER_TARGET_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; PRINTER_DATA_SET_ATTRIBS ShowRegisterDataSetAttribs = { &ShowRegisterListAttributes, NULL }; /** Command syntax definition **/ struct Command ShowRegisterCommand = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #else {L"", L"", L"", L"",L"", FALSE, ValueOptional} #endif }, { //!< targets {DIMM_TARGET, L"", L"DimmIDs", TRUE, ValueOptional}, {REGISTER_TARGET, L"", L"Register", TRUE, ValueOptional}, }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show Key " PMEM_MODULE_STR L" Registers.", //!< help ShowRegister, TRUE //!< enable print control support }; CHAR16 *mppAllowedShowDimmRegistersValues[] = { REGISTER_BSR_STR, REGISTER_OS_STR }; /** Register the show register command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowRegisterCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowRegisterCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Execute the show pools command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED Failure invoking CONFIG_PROTOCOL function **/ EFI_STATUS ShowRegister( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; UINT16 *pDimmIds = NULL; UINT32 DimmIdsNum = 0; CHAR16 *pRegisterValues = NULL; CHAR16 *pDimmValues = NULL; UINT32 Index = 0; COMMAND_STATUS *pCommandStatus = NULL; BOOLEAN ShowAllRegisters = FALSE; DIMM_BSR Bsr; UINT32 DimmCount = 0; DIMM_INFO *pDimms = NULL; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; UINT32 DimmIndex = 0; UINT32 RegIndex = 0; NVDIMM_ENTRY(); ZeroMem(DimmStr, sizeof(DimmStr)); Bsr.AsUint64 = 0; if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; /** Make sure we can access the config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); ReturnCode = EFI_NOT_FOUND; goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } /** if a specific DIMM pid was passed in, set it **/ pDimmValues = GetTargetValue(pCmd, DIMM_TARGET); if (pDimmValues != NULL) { if (StrLen(pDimmValues) > 0) { ReturnCode = GetDimmIdsFromString(pCmd, pDimmValues, pDimms, DimmCount, &pDimmIds, &DimmIdsNum); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Target value is not a valid DIMM ID"); goto Finish; } } else { DimmIdsNum = 0; } } else { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_WARN("Missing Dimm target"); PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INCOMPLETE_SYNTAX); goto Finish; } if (ContainTarget(pCmd, REGISTER_TARGET)) { pRegisterValues = GetTargetValue(pCmd, REGISTER_TARGET); if (StrLen(pRegisterValues) == 0) { ShowAllRegisters = TRUE; } else { ShowAllRegisters = FALSE; } } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INCOMPLETE_SYNTAX); goto Finish; } CHECK_RESULT(InitializeCommandStatus(&pCommandStatus), Finish); /** check that the register parameters are correct if provided**/ if (!ShowAllRegisters) { ReturnCode = CheckDisplayList(pRegisterValues, mppAllowedShowDimmRegistersValues, ALLOWED_DISP_VALUES_COUNT(mppAllowedShowDimmRegistersValues)); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_TARGET_REGISTER); goto Finish; } } /** Get and print registers for each requested dimm **/ for (Index = 0; Index < DimmCount; Index++) { if (DimmIdsNum > 0 && !ContainUint(pDimmIds, DimmIdsNum, pDimms[Index].DimmID)) { NVDIMM_WARN("Dimm 0x%x not found", pDimms[Index].DimmHandle); continue; } ReturnCode = pNvmDimmConfigProtocol->RetrieveDimmRegisters(pNvmDimmConfigProtocol, pDimms[Index].DimmID, &Bsr.AsUint64, NULL, pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to retrieve Dimm Registers"); goto Finish; } ReturnCode = GetPreferredDimmIdAsString(pDimms[Index].DimmHandle, pDimms[Index].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_INDEX_PATH, DimmIndex); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, DimmStr); if (ContainsValue(pRegisterValues, REGISTER_BSR_STR) || ShowAllRegisters) { PRINTER_BUILD_KEY_PATH(pPath, DS_REGISTER_INDEX_PATH, DimmIndex, RegIndex); RegIndex++; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, REGISTER_TARGET_STR, REGISTER_BSR_STR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Boot Status", FORMAT_HEX_PREFIX FORMAT_UINT64_HEX, Bsr.AsUint64); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [07:00] MajorCheckpoint --------------------------", FORMAT_HEX_NOWIDTH, Bsr.Separated_Current_FIS.Major); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [15:08] MinorCheckpoint --------------------------", FORMAT_HEX_NOWIDTH, Bsr.Separated_Current_FIS.Minor); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [17:16] MR (Media Ready) -------------------------", FORMAT_HEX_NOWIDTH L" (00:notReady; 1:Ready; 2:Error; 3:Rsv)", Bsr.Separated_Current_FIS.MR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [18:18] DT (DDRT IO Init Started) ----------------", FORMAT_HEX_NOWIDTH L" (0:notStarted; 1:Training Started)", Bsr.Separated_Current_FIS.DT); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [19:19] PCR (PCR access locked) ------------------", FORMAT_HEX_NOWIDTH L" (0:Unlocked; 1:Locked)", Bsr.Separated_Current_FIS.PCR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [20:20] MBR (Mailbox Ready) ----------------------", FORMAT_HEX_NOWIDTH L" (0:notReady; 1:Ready)", Bsr.Separated_Current_FIS.MBR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [21:21] WTS (Watchdog Status)---------------------", FORMAT_HEX_NOWIDTH L" (0:noChange; 1:WT NMI generated)", Bsr.Separated_Current_FIS.WTS); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [22:22] FRCF (First Fast Refresh Completed) ------", FORMAT_HEX_NOWIDTH L" (0:noChange; 1:1stRefreshCycleCompleted)", Bsr.Separated_Current_FIS.FRCF); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [23:23] CR (Credit Ready) ------------------------", FORMAT_HEX_NOWIDTH L" (0:WDB notFlushed; 1:WDB Flushed)", Bsr.Separated_Current_FIS.CR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [24:24] MD (Media Disabled) ----------------------", FORMAT_HEX_NOWIDTH L" (0:User Data is accessible; 1:User Data is not accessible)", Bsr.Separated_Current_FIS.MD); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [25:25] SVNDE (SVN Downgrade Opt-In Enable) ------", FORMAT_HEX_NOWIDTH L" (0:Not Enabled; 1:Enabled)", Bsr.Separated_Current_FIS.SVNDE); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [26:26] SVNCOIS (SVN Downgrade Opt-In Was Enabled)", FORMAT_HEX_NOWIDTH L" (0:Never Enabled; 1:Has Been Enabled)", Bsr.Separated_Current_FIS.SVNCOIS); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [28:27] DR (DRAM Ready(AIT)) ---------------------", FORMAT_HEX_NOWIDTH L" (0:Not trained,Not Loaded; 1:Trained,Not Loaded; 2:Error; 3:Trained,Loaded(Ready))", Bsr.Separated_Current_FIS.DR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [29:29] RR (Reboot Required) ---------------------", FORMAT_HEX_NOWIDTH L" (0:No reset is needed by the " PMEM_MODULE_STR L"; 1:The " PMEM_MODULES_STR L" internal state requires a platform power cycle)", Bsr.Separated_Current_FIS.RR); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [30:30] LFOPB ------------------------------------", FORMAT_HEX_NOWIDTH L" (0:No Error; 1:Fatal Link Error)", Bsr.Separated_Current_FIS.LFOPB); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [31:31] SVNWC ------------------------------------", FORMAT_HEX_NOWIDTH L" (0:The SVN Opt-In Window is open; 1:The SVN Opt-In Window is closed)", Bsr.Separated_Current_FIS.SVNWC); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [33:32] Rsvd -------------------------------------", FORMAT_HEX_NOWIDTH L" (Rsvd)", Bsr.Separated_Current_FIS.Rsvd); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [35:34] DTS --------------------------------------", FORMAT_HEX_NOWIDTH L" (00:Training Not Complete; 1:Training Complete; 2:Training Failure; 3:S3 Complete)", Bsr.Separated_Current_FIS.DTS); if ((pDimms[DimmIndex].FwVer.FwApiMajor > 2) || (pDimms[DimmIndex].FwVer.FwApiMajor == 2 && pDimms[DimmIndex].FwVer.FwApiMinor >= 3)) { // Current FIS version PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [36:36] FAC --------------------------------------", FORMAT_HEX_NOWIDTH L" (0:FW Activate has not completed; 1:FW Activate has completed)\n", Bsr.Separated_Current_FIS.FAC); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [63:37] Rsvd1 ------------------------------------", FORMAT_HEX_NOWIDTH L" (Rsvd1)\n", Bsr.Separated_Current_FIS.Rsvd1); } else { // FIS version 2.2 or earlier detected PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L" [63:36] Rsvd1 ------------------------------------", FORMAT_HEX_NOWIDTH L" (Rsvd1)\n", Bsr.Separated_FIS_1_15.Rsvd1); } } DimmIndex++; } ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); PRINTER_SET_COMMAND_STATUS(pPrinterCtx, ReturnCode, CLI_INFO_SHOW_REGISTER, L" on", pCommandStatus); //Specify DataSet attributes PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowRegisterDataSetAttribs); //Force as list PRINTER_ENABLE_LIST_TABLE_FORMAT(pPrinterCtx); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pPath); FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pDimmIds); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowRegisterCommand.h000066400000000000000000000015371440615110200220120ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_REGISTER_COMMAND_H_ #define _SHOW_REGISTER_COMMAND_H_ #include "CommandParser.h" #define FW_MB_SMALL_OUTPUT_REG_USED 1 /** Register the show register command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowRegisterCommand( ); /** Execute the show register command @param[in] pCmd command from CLI @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS ShowRegister( IN struct Command *pCmd ); #endif /* _SHOW_REGISTER_COMMAND_H_ */ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowSensorCommand.c000066400000000000000000000445411440615110200214740ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "ShowSensorCommand.h" #include #include #include #include #include "Common.h" #include #include "NvmDimmCli.h" #include #include #include #include #define DIMM_ID_STR L"DimmID" #define SENSOR_TYPE_STR L"Type" #define CURRENT_VALUE_STR L"CurrentValue" #define ALARM_THRESHOLD_STR L"AlarmThreshold" #define THROTTLING_STOP_THRESHOLD_STR L"ThrottlingStopThreshold" #define THROTTLING_START_THRESHOLD_STR L"ThrottlingStartThreshold" #define SHUTDOWN_THRESHOLD_STR L"ShutdownThreshold" #define MAX_TEMPERATURE L"MaxTemperature" #define DISABLED_STR L"Disabled" #define DS_ROOT_PATH L"/SensorList" #define DS_DIMM_PATH L"/SensorList/Dimm" #define DS_DIMM_INDEX_PATH L"/SensorList/Dimm[%d]" #define DS_SENSOR_PATH L"/SensorList/Dimm/Sensor" #define DS_SENSOR_INDEX_PATH L"/SensorList/Dimm[%d]/Sensor[%d]" /* * PRINT LIST ATTRIBUTES (2 levels: Dimm-->Sensor) * ---DimmId=0x0001--- * ---Type=Health * CurrentVal=Healthy * CurrentState=Normal * ... */ PRINTER_LIST_ATTRIB ShowSensorListAttributes = { { { DIMM_NODE_STR, //GROUP LEVEL TYPE L"---" DIMM_ID_STR L"=$(" DIMM_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT FORMAT_STR L"=" FORMAT_STR, //NULL or KEY VAL FORMAT STR DIMM_ID_STR //NULL or IGNORE KEY LIST (K1;K2) }, { SENSOR_NODE_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT L"---" SENSOR_TYPE_STR L"=$(" SENSOR_TYPE_STR L")", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR L"=" FORMAT_STR, //NULL or KEY VAL FORMAT STR SENSOR_TYPE_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; /* * PRINTER TABLE ATTRIBUTES (4 columns) * DimmID | Type | CurrentValue | CurrentState * =========================================== * 0x0001 | X | X | X * ... */ PRINTER_TABLE_ATTRIB ShowSensorTableAttributes = { { { DIMM_ID_STR, //COLUMN HEADER DIMM_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_PATH PATH_KEY_DELIM DIMM_ID_STR //COLUMN DATA PATH }, { SENSOR_TYPE_STR, //COLUMN HEADER SENSOR_TYPE_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_SENSOR_PATH PATH_KEY_DELIM SENSOR_TYPE_STR //COLUMN DATA PATH }, { CURRENT_VALUE_STR, //COLUMN HEADER SENSOR_VALUE_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_SENSOR_PATH PATH_KEY_DELIM CURRENT_VALUE_STR //COLUMN DATA PATH } } }; PRINTER_DATA_SET_ATTRIBS ShowSensorDataSetAttribs = { &ShowSensorListAttributes, &ShowSensorTableAttributes }; /** Command syntax definition **/ struct Command ShowSensorCommand = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {ALL_OPTION_SHORT, ALL_OPTION, L"", L"", HELP_ALL_DETAILS_TEXT, FALSE, ValueEmpty}, {DISPLAY_OPTION_SHORT, DISPLAY_OPTION, L"", HELP_TEXT_ATTRIBUTES, HELP_DISPLAY_DETAILS_TEXT, FALSE, ValueRequired} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, { {SENSOR_TARGET, L"", HELP_TEXT_SENSORS, TRUE, ValueOptional}, {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional} //!< targets }, { {L"", L"", L"", FALSE, ValueOptional}, }, //!< properties L"Show health statistics.", //!< help ShowSensor, TRUE, //!< enable print control support }; CHAR16 *mppAllowedShowSensorDisplayValues[] = { DIMM_ID_STR, SENSOR_TYPE_STR, CURRENT_VALUE_STR, ALARM_THRESHOLD_STR, THROTTLING_STOP_THRESHOLD_STR, THROTTLING_START_THRESHOLD_STR, SHUTDOWN_THRESHOLD_STR, ALARM_ENABLED_PROPERTY, MAX_TEMPERATURE }; /** Create the string from value for a sensor. param[in] Value is the value to be printed. param[in] SensorType - type of sensor **/ STATIC CHAR16 * GetSensorValue( IN INT64 Value, IN UINT8 SensorType ) { CHAR16 *pReturnBuffer = NULL; pReturnBuffer = CatSPrintClean(pReturnBuffer, L"%lld" FORMAT_STR L"", Value, SensorValueMeasure(SensorType)); return pReturnBuffer; } /** Execute the show sensor command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS ShowSensor( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; UINT16 *pDimmIds = NULL; UINT32 DimmIdsNum = 0; CHAR16 *pDimmsValue = NULL; UINT32 DimmsCount = 0; DIMM_INFO *pDimms = NULL; UINT32 DimmIndex = 0; BOOLEAN Found = FALSE; UINT32 SensorIndex = 0; CHAR16 *pTempBuff = NULL; UINT32 SensorToDisplay = SENSOR_TYPE_ALL; COMMAND_STATUS *pCommandStatus = NULL; DIMM_SENSOR DimmSensorsSet[SENSOR_TYPE_COUNT]; CHAR16 *pTargetValue = NULL; DISPLAY_PREFERENCES DisplayPreferences; CMD_DISPLAY_OPTIONS *pDispOptions = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; BOOLEAN FIS_1_13 = FALSE; struct { CHAR16 *pSensorStr; UINT32 Sensor; } Sensors[] = { {CONTROLLER_TEMPERATURE_STR, SENSOR_TYPE_CONTROLLER_TEMPERATURE}, {MEDIA_TEMPERATURE_STR, SENSOR_TYPE_MEDIA_TEMPERATURE}, {SPARE_CAPACITY_STR, SENSOR_TYPE_PERCENTAGE_REMAINING}, {POWER_CYCLES_STR, SENSOR_TYPE_POWER_CYCLES}, {POWER_ON_TIME_STR, SENSOR_TYPE_POWER_ON_TIME}, {LATCHED_DIRTY_SHUTDOWN_COUNT_STR, SENSOR_TYPE_LATCHED_DIRTY_SHUTDOWN_COUNT}, {UPTIME_STR, SENSOR_TYPE_UP_TIME}, {FW_ERROR_COUNT_STR, SENSOR_TYPE_FW_ERROR_COUNT}, {DIMM_HEALTH_STR, SENSOR_TYPE_DIMM_HEALTH}, {UNLATCHED_DIRTY_SHUTDOWN_COUNT_STR, SENSOR_TYPE_UNLATCHED_DIRTY_SHUTDOWN_COUNT}, }; UINT32 SensorsNum = ARRAY_SIZE(Sensors); CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; NVDIMM_ENTRY(); ZeroMem(DimmSensorsSet, sizeof(DimmSensorsSet)); ZeroMem(DimmStr, sizeof(DimmStr)); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; pDispOptions = AllocateZeroPool(sizeof(CMD_DISPLAY_OPTIONS)); if (NULL == pDispOptions) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = CheckAllAndDisplayOptions(pCmd, mppAllowedShowSensorDisplayValues, ALLOWED_DISP_VALUES_COUNT(mppAllowedShowSensorDisplayValues), pDispOptions); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CheckAllAndDisplayOptions has returned error. Code " FORMAT_EFI_STATUS "\n", ReturnCode); goto Finish; } ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // initialize status structure ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed on InitializeCommandStatus"); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmsCount); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } if (ContainTarget(pCmd, DIMM_TARGET)) { pDimmsValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pDimmsValue, pDimms, DimmsCount, &pDimmIds, &DimmIdsNum); if (EFI_ERROR(ReturnCode)) { goto Finish; } } /** The user has provided a sensor. Try to match it to our list. Return an error if the sensor name is invalid. **/ pTargetValue = GetTargetValue(pCmd, SENSOR_TARGET); if (pTargetValue != NULL && StrLen(pTargetValue) > 0) { Found = FALSE; for (DimmIndex = 0; DimmIndex < SensorsNum; DimmIndex++) { if (StrICmp(pTargetValue, Sensors[DimmIndex].pSensorStr) == 0) { SensorToDisplay = Sensors[DimmIndex].Sensor; Found = TRUE; break; } } if (!Found) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"The provided sensor: " FORMAT_STR L" is not valid.\n", pTargetValue); goto Finish; } } for (DimmIndex = 0; DimmIndex < DimmsCount; DimmIndex++) { if (DimmIdsNum > 0 && !ContainUint(pDimmIds, DimmIdsNum, pDimms[DimmIndex].DimmID)) { continue; } ReturnCode = GetPreferredDimmIdAsString(pDimms[DimmIndex].DimmHandle, pDimms[DimmIndex].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to translate " PMEM_MODULE_STR L" identifier to string\n"); goto Finish; } ReturnCode = GetSensorsInfo(pNvmDimmConfigProtocol, pDimms[DimmIndex].DimmID, DimmSensorsSet); if (EFI_ERROR(ReturnCode)) { /** We do not return on error. Just inform the user and skip to the next PMem module or end. **/ if (ReturnCode == EFI_NOT_READY) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to read the sensors or thresholds values from " PMEM_MODULE_STR L" " FORMAT_STR L" - " PMEM_MODULE_STR L" is unmanageable.\n", DimmStr); } else { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to read the sensors or thresholds values from " PMEM_MODULE_STR L" " FORMAT_STR L". Code: " FORMAT_EFI_STATUS "\n", DimmStr, ReturnCode); } continue; } PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_INDEX_PATH, DimmIndex); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, DimmStr); //Checking the FIS Version if ((pDimms[DimmIndex].FwVer.FwApiMajor >= 2 )||(pDimms[DimmIndex].FwVer.FwApiMajor == 1 && pDimms[DimmIndex].FwVer.FwApiMinor >= 13)) { FIS_1_13 = TRUE; } for (SensorIndex = 0; SensorIndex < SENSOR_TYPE_COUNT; SensorIndex++) { if ((SensorToDisplay != SENSOR_TYPE_ALL && DimmSensorsSet[SensorIndex].Type != SensorToDisplay)) { continue; } PRINTER_BUILD_KEY_PATH(pPath, DS_SENSOR_INDEX_PATH, DimmIndex, SensorIndex); /** Type **/ PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SENSOR_TYPE_STR, SensorTypeToString(DimmSensorsSet[SensorIndex].Type)); /** Value **/ if (!pDispOptions->DisplayOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, CURRENT_VALUE_STR))) { /** Only for Health State **/ if (ContainsValue(SensorTypeToString(DimmSensorsSet[SensorIndex].Type), DIMM_HEALTH_STR)) { pTempBuff = HealthToString(gNvmDimmCliHiiHandle, (UINT8)DimmSensorsSet[SensorIndex].Value); if (pTempBuff == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } } else { pTempBuff = GetSensorValue(DimmSensorsSet[SensorIndex].Value, DimmSensorsSet[SensorIndex].Type); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, CURRENT_VALUE_STR, pTempBuff); FREE_POOL_SAFE(pTempBuff); } /** AlarmThreshold **/ if (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ALARM_THRESHOLD_STR))) { switch (SensorIndex) { case SENSOR_TYPE_MEDIA_TEMPERATURE: case SENSOR_TYPE_CONTROLLER_TEMPERATURE: case SENSOR_TYPE_PERCENTAGE_REMAINING: // Only media, controller, and percentage possess alarm thresholds pTempBuff = GetSensorValue(DimmSensorsSet[SensorIndex].AlarmThreshold, DimmSensorsSet[SensorIndex].Type); break; default: pTempBuff = NULL; break; } if (NULL != pTempBuff) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ALARM_THRESHOLD_STR, pTempBuff); FREE_POOL_SAFE(pTempBuff); } } /** AlarmEnabled **/ if (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, ALARM_ENABLED_PROPERTY))) { switch (SensorIndex) { case SENSOR_TYPE_MEDIA_TEMPERATURE: case SENSOR_TYPE_CONTROLLER_TEMPERATURE: case SENSOR_TYPE_PERCENTAGE_REMAINING: // Only media, controller, and percentage possess alarm thresholds PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, ALARM_ENABLED_PROPERTY, SensorEnabledStateToString(DimmSensorsSet[SensorIndex].Enabled)); break; default: //do nothing break; } } /** ThrottlingStopThreshold **/ if (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, THROTTLING_STOP_THRESHOLD_STR))) { switch (SensorIndex) { case SENSOR_TYPE_CONTROLLER_TEMPERATURE: case SENSOR_TYPE_MEDIA_TEMPERATURE: // Only Media temperature sensor got lower critical threshold pTempBuff = GetSensorValue(DimmSensorsSet[SensorIndex].ThrottlingStopThreshold, DimmSensorsSet[SensorIndex].Type); break; default: pTempBuff = NULL; break; } if (NULL != pTempBuff) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, THROTTLING_STOP_THRESHOLD_STR, pTempBuff); FREE_POOL_SAFE(pTempBuff); } } /** ThrottlingStartThreshold **/ if (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, THROTTLING_START_THRESHOLD_STR))) { switch (SensorIndex) { case SENSOR_TYPE_CONTROLLER_TEMPERATURE: case SENSOR_TYPE_MEDIA_TEMPERATURE: // Only Media temperature sensor got upper critical threshold pTempBuff = GetSensorValue(DimmSensorsSet[SensorIndex].ThrottlingStartThreshold, DimmSensorsSet[SensorIndex].Type); break; default: pTempBuff = NULL; break; } if (NULL != pTempBuff) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, THROTTLING_START_THRESHOLD_STR, pTempBuff); FREE_POOL_SAFE(pTempBuff); } } /** ShutdownThreshold **/ if (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, SHUTDOWN_THRESHOLD_STR))) { switch (SensorIndex) { case SENSOR_TYPE_CONTROLLER_TEMPERATURE: case SENSOR_TYPE_MEDIA_TEMPERATURE: // Only Controller/Media temperature sensor got upper fatal threshold pTempBuff = GetSensorValue(DimmSensorsSet[SensorIndex].ShutdownThreshold, DimmSensorsSet[SensorIndex].Type); break; default: pTempBuff = NULL; break; } if (NULL != pTempBuff) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SHUTDOWN_THRESHOLD_STR, pTempBuff); FREE_POOL_SAFE(pTempBuff); } } /** MaxTemperature **/ if (pDispOptions->AllOptionSet || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MAX_TEMPERATURE))) { switch (SensorIndex) { case SENSOR_TYPE_CONTROLLER_TEMPERATURE: case SENSOR_TYPE_MEDIA_TEMPERATURE: // Only Controller/Media temperature sensor have MaxTemperature attribute (FIS 1.13+) if (FIS_1_13) { pTempBuff = GetSensorValue(DimmSensorsSet[SensorIndex].MaxTemperature, DimmSensorsSet[SensorIndex].Type); } else { pTempBuff = CatSPrintClean(NULL, FORMAT_STR, NOT_APPLICABLE_SHORT_STR); } break; default: pTempBuff = NULL; break; } if (NULL != pTempBuff) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MAX_TEMPERATURE, pTempBuff); FREE_POOL_SAFE(pTempBuff); } } } } //Specify table attributes PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowSensorDataSetAttribs); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pPath); FREE_CMD_DISPLAY_OPTIONS_SAFE(pDispOptions); FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pDimmIds); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the set sensor command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowSensorCommand( ) { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowSensorCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowSensorCommand.h000066400000000000000000000014301440615110200214670ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_SENSOR_COMMAND_H_ #define _SHOW_SENSOR_COMMAND_H_ #include #include "CommandParser.h" #include /** Register the show sensor command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowSensorCommand( ); /** Execute the show sensor command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS ShowSensor( IN struct Command *pCmd ); #endif /** _SHOW_SENSOR_COMMAND_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowSessionCommand.c000066400000000000000000000150101440615110200216330ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "ShowSessionCommand.h" #include #include #include #include #include #include "Common.h" #include #include #ifdef OS_BUILD #include "os.h" #endif #define DS_ROOT_PATH L"/Session" #define DS_TAG_PATH L"/Session/Tag" #define DS_TAG_INDEX_PATH L"/Session/Tag[%d]" #define TAG_ID_FORMAT L"0x%x" #define TAG_ID_SELECTED_FORMAT L"0x%x*" EFI_STATUS MapTagToCurrentSessionState( IN EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol, OUT UINT32 *pTag ); /* * PRINT LIST ATTRIBUTES * ---TagId=0x0001--- * CliArgs= */ PRINTER_LIST_ATTRIB ShowSessionListAttributes = { { { TAG_STR, //GROUP LEVEL TYPE L"---" TAG_ID_STR L"=$(" TAG_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR TAG_ID_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; /* * PRINTER TABLE ATTRIBUTES (3 columns) * TagID | ExitCode | CliArgs * ======================================================================== * 0x0001 | X | X * ... */ PRINTER_TABLE_ATTRIB ShowSessionTableAttributes = { { { TAG_ID_STR, //COLUMN HEADER DEFAULT_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_TAG_PATH PATH_KEY_DELIM TAG_ID_STR //COLUMN DATA PATH }, { CLI_ARGS_STR, //COLUMN HEADER DEFAULT_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_TAG_PATH PATH_KEY_DELIM CLI_ARGS_STR //COLUMN DATA PATH } } }; PRINTER_DATA_SET_ATTRIBS ShowSessionDataSetAttribs = { &ShowSessionListAttributes, &ShowSessionTableAttributes }; /** Command syntax definition **/ struct Command ShowSessionCommand = { SHOW_VERB, //!< verb { #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired }, #endif {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", L"", L"", L"",FALSE, ValueOptional} }, //!< options {{SESSION_TARGET, L"", L"", TRUE, ValueEmpty}}, //!< targets {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show information about the current playback or record (PBR) session.", //!< help ShowSession, TRUE, TRUE //exclude from PBR }; /** Execute the show host server command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS ShowSession( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol = NULL; DISPLAY_PREFERENCES DisplayPreferences; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; UINT32 TagCount = 0; UINT32 Index = 0; CHAR16 *pName = NULL; CHAR16 *pDescription = NULL; CHAR16 *pTagId = NULL; UINT32 TagId = INVALID_TAG_ID; UINT32 Signature; NVDIMM_ENTRY(); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; //If Windows, check for admin privilege needed to update registry for PBR state CHECK_WIN_ADMIN_PERMISSIONS(); ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } /** Make sure we can access the config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmPbrProtocolGuid, (VOID **)&pNvmDimmPbrProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } //Retrieve the current TagID (CLI's job to track/increment/reset the tag id). PbrDcpmmDeserializeTagId(&TagId, 0); ReturnCode = pNvmDimmPbrProtocol->PbrGetTagCount(&TagCount); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_GET_SESSION_TAG_COUNT); goto Finish; } for (Index = 0; Index < TagCount; ++Index) { if (Index == TagId) { pTagId = CatSPrintClean(NULL, TAG_ID_SELECTED_FORMAT, Index); } else { pTagId = CatSPrintClean(NULL, TAG_ID_FORMAT, Index); } PRINTER_BUILD_KEY_PATH(pPath, DS_TAG_INDEX_PATH, Index); ReturnCode = pNvmDimmPbrProtocol->PbrGetTag(Index, &Signature, &pName, &pDescription, NULL, NULL); if (ReturnCode == EFI_SUCCESS) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, TAG_ID_STR, pTagId); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, CLI_ARGS_STR, pName); } FREE_POOL_SAFE(pTagId); FREE_POOL_SAFE(pName); FREE_POOL_SAFE(pDescription); } //Specify table attributes PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowSessionDataSetAttribs); PRINTER_ENABLE_TEXT_TABLE_FORMAT(pPrinterCtx); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); NVDIMM_EXIT_I64(ReturnCode); FREE_POOL_SAFE(pPath); FREE_POOL_SAFE(pTagId); FREE_POOL_SAFE(pName); FREE_POOL_SAFE(pDescription); return ReturnCode; } /** Register the show session command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowSessionCommand( EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowSessionCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowSessionCommand.h000066400000000000000000000014401440615110200216420ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_SESSION_COMMAND_ #define _SHOW_SESSION_COMMAND_ #include "CommandParser.h" /** Execute the show host server command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS ShowSession( IN struct Command *pCmd ); /** Register the show session command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowSessionCommand( ); #endif /* _SHOW_SESSION_COMMAND_ */ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowSmbiosCommand.h000066400000000000000000000006441440615110200214600ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_SMBIOS_COMMAND_H_ #define _SHOW_SMBIOS_COMMAND_H_ #include "CommandParser.h" /** Register the show SMBIOS tables command. **/ EFI_STATUS registerShowSmbiosCommand( ); /** Execute the show SMBIOS command. **/ EFI_STATUS showSmbios( IN struct Command *pCmd ); #endif /** _SHOW_SMBIOS_COMMAND_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowSocketsCommand.c000066400000000000000000000247651440615110200216440ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "Debug.h" #include "Types.h" #include "Utility.h" #include "NvmDimmCli.h" #include "NvmInterface.h" #include "CommandParser.h" #include "ShowSocketsCommand.h" #include "Common.h" #include "Convert.h" #include #define DS_ROOT_PATH L"/SocketList" #define DS_SOCKET_PATH L"/SocketList/Socket" #define DS_SOCKET_INDEX_PATH L"/SocketList/Socket[%d]" /* * PRINT LIST ATTRIBUTES * ---SocketId=0x0001--- * MappedMemoryLimit=X * TotalMappedMemory=X * ... */ PRINTER_LIST_ATTRIB ShowSocketListAttributes = { { { SOCKET_NODE_STR, //GROUP LEVEL TYPE L"---" SOCKET_ID_STR L"=$(" SOCKET_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR SOCKET_ID_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; /* * PRINTER TABLE ATTRIBUTES (3 columns) * SocketID | MappedMemoryLimit | TotalMappedMemory * ================================================ * 0x0001 | X | X * ... */ PRINTER_TABLE_ATTRIB ShowSocketTableAttributes = { { { SOCKET_ID_STR, //COLUMN HEADER SOCKET_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_SOCKET_PATH PATH_KEY_DELIM SOCKET_ID_STR //COLUMN DATA PATH }, { MAPPED_MEMORY_LIMIT_STR, //COLUMN HEADER MAPPED_MEMORY_LIMIT_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_SOCKET_PATH PATH_KEY_DELIM MAPPED_MEMORY_LIMIT_STR //COLUMN DATA PATH }, { TOTAL_MAPPED_MEMORY_STR, //COLUMN HEADER TOTAL_MAPPED_MEMORY_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_SOCKET_PATH PATH_KEY_DELIM TOTAL_MAPPED_MEMORY_STR //COLUMN DATA PATH } } }; PRINTER_DATA_SET_ATTRIBS ShowSocketDataSetAttribs = { &ShowSocketListAttributes, &ShowSocketTableAttributes }; /** Command syntax definition **/ struct Command ShowSocketsCommand = { SHOW_VERB, //!< verb /** options **/ { {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"", HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {ALL_OPTION_SHORT, ALL_OPTION, L"", L"", HELP_ALL_DETAILS_TEXT, FALSE, ValueEmpty}, {DISPLAY_OPTION_SHORT, DISPLAY_OPTION, L"", HELP_TEXT_ATTRIBUTES, HELP_DISPLAY_DETAILS_TEXT, FALSE, ValueRequired}, {UNITS_OPTION_SHORT, UNITS_OPTION, L"", UNITS_OPTION_HELP, HELP_UNIT_DETAILS_TEXT, FALSE, ValueRequired} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, /** targets **/ { {SOCKET_TARGET, L"", HELP_TEXT_SOCKET_IDS, TRUE, ValueOptional} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show mapped memory limit and total mapped memory of each socket.", //!< help ShowSockets, //!< run function TRUE, //!< enable print control support }; CHAR16 *mppAllowedShowSocketsDisplayValues[] = { SOCKET_ID_STR, MAPPED_MEMORY_LIMIT_STR, TOTAL_MAPPED_MEMORY_STR }; /** Register the show sockets command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowSocketsCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowSocketsCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Execute the show sockets command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_NOT_FOUND failed to open Config protocol, or run-time preferences could not be retrieved, or user-specified socket ID is invalid @retval Other errors returned by the driver **/ EFI_STATUS ShowSockets( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; UINT32 SocketCount = 0; SOCKET_INFO *pSockets = NULL; UINT16 *pSocketIds = NULL; UINT32 SocketIdsNum = 0; CHAR16 *pSocketsValue = NULL; UINT32 Index = 0; UINT32 Index2 = 0; UINT16 UnitsOption = DISPLAY_SIZE_UNIT_UNKNOWN; UINT16 UnitsToDisplay = FixedPcdGet16(PcdDcpmmCliDefaultCapacityUnit); DISPLAY_PREFERENCES DisplayPreferences; CHAR16 *pMappedMemLimitStr = NULL; CHAR16 *pTotalMappedMemStr = NULL; BOOLEAN SocketIdFound = FALSE; CMD_DISPLAY_OPTIONS *pDispOptions = NULL; BOOLEAN ShowAll = FALSE; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; NVDIMM_ENTRY(); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; pDispOptions = AllocateZeroPool(sizeof(CMD_DISPLAY_OPTIONS)); if (NULL == pDispOptions) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = CheckAllAndDisplayOptions(pCmd, mppAllowedShowSocketsDisplayValues, ALLOWED_DISP_VALUES_COUNT(mppAllowedShowSocketsDisplayValues), pDispOptions); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CheckAllAndDisplayOptions has returned error. Code " FORMAT_EFI_STATUS "\n", ReturnCode); goto Finish; } ShowAll = (!pDispOptions->AllOptionSet && !pDispOptions->DisplayOptionSet) || pDispOptions->AllOptionSet; /** Make sure we can access the config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } /** if Socket IDs were passed in, read them **/ if (pCmd->targets[0].pTargetValueStr && StrLen(pCmd->targets[0].pTargetValueStr) > 0) { pSocketsValue = GetTargetValue(pCmd, SOCKET_TARGET); ReturnCode = GetUintsFromString(pSocketsValue, &pSocketIds, &SocketIdsNum); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_TARGET_SOCKET); goto Finish; } } /** Determine the units to display the sizes in **/ ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } UnitsToDisplay = DisplayPreferences.SizeUnit; ReturnCode = GetUnitsOption(pCmd, &UnitsOption); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Any valid units option will override the preferences **/ if (UnitsOption != DISPLAY_SIZE_UNIT_UNKNOWN) { UnitsToDisplay = UnitsOption; } /** Retrieve the list of sockets on the platform **/ ReturnCode = pNvmDimmConfigProtocol->GetSockets(pNvmDimmConfigProtocol, &SocketCount, &pSockets); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } else if (pSockets == NULL) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_SOCKET_SKU_SUPPORT); goto Finish; } /** Check if proper -socket target is given **/ for (Index = 0; Index < SocketIdsNum; Index++) { SocketIdFound = FALSE; /** Checking if the specified socket exist **/ for (Index2 = 0; Index2 < SocketCount; Index2++) { if (pSockets[Index2].SocketId == pSocketIds[Index]) { SocketIdFound = TRUE; break; } } if (!SocketIdFound) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_SOCKET_NOT_FOUND CLI_ERR_INCORRECT_VALUE_TARGET_SOCKET, pSocketIds[Index]); goto Finish; } } for (Index = 0; Index < SocketCount; Index++) { if (SocketIdsNum > 0 && !ContainUint(pSocketIds, SocketIdsNum, pSockets[Index].SocketId)) { continue; } PRINTER_BUILD_KEY_PATH(pPath, DS_SOCKET_INDEX_PATH, Index); /** SocketID **/ PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SOCKET_ID_STR, FORMAT_HEX, pSockets[Index].SocketId); /** MappedMemoryLimit **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, MAPPED_MEMORY_LIMIT_STR))) { ReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pSockets[Index].MappedMemoryLimit, UnitsToDisplay, TRUE, &pMappedMemLimitStr); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_CAPACITY_STRING); goto Finish; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MAPPED_MEMORY_LIMIT_STR, pMappedMemLimitStr); FREE_POOL_SAFE(pMappedMemLimitStr); } /** TotalMappedMemory **/ if (ShowAll || (pDispOptions->DisplayOptionSet && ContainsValue(pDispOptions->pDisplayValues, TOTAL_MAPPED_MEMORY_STR))) { ReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pSockets[Index].TotalMappedMemory, UnitsToDisplay, TRUE, &pTotalMappedMemStr); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_CAPACITY_STRING); goto Finish; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, TOTAL_MAPPED_MEMORY_STR, pTotalMappedMemStr); FREE_POOL_SAFE(pTotalMappedMemStr); } } //Specify table attributes PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowSocketDataSetAttribs); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pPath); FREE_CMD_DISPLAY_OPTIONS_SAFE(pDispOptions); FREE_POOL_SAFE(pSockets); FREE_POOL_SAFE(pSocketIds); FREE_POOL_SAFE(pMappedMemLimitStr); FREE_POOL_SAFE(pTotalMappedMemStr); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowSocketsCommand.h000066400000000000000000000020601440615110200216310ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_SOCKETS_COMMAND_H_ #define _SHOW_SOCKETS_COMMAND_H_ #include "CommandParser.h" #define MAPPED_MEMORY_LIMIT_STR L"MappedMemoryLimit" #define TOTAL_MAPPED_MEMORY_STR L"TotalMappedMemory" /** Register the show sockets command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowSocketsCommand( ); /** Execute the show sockets command @param[in] pCmd command from CLI @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_NOT_FOUND failed to open Config protocol, or run-time preferences could not be retrieved, or user-specified socket ID is invalid @retval Other errors returned by the driver **/ EFI_STATUS ShowSockets( IN struct Command *pCmd ); #endif /* _SHOW_SOCKETS_COMMAND_H_*/ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowSystemCapabilitiesCommand.c000066400000000000000000000415771440615110200240270ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "ShowSystemCapabilitiesCommand.h" #include #include #include #include #include #include "Common.h" #include "NvmDimmCli.h" #include #define DS_ROOT_PATH L"/SystemCapabilities" /** Command syntax definition **/ struct Command ShowSystemCapabilitiesCommand = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {ALL_OPTION_SHORT, ALL_OPTION, L"", L"",HELP_ALL_DETAILS_TEXT, FALSE, ValueEmpty}, {DISPLAY_OPTION_SHORT, DISPLAY_OPTION, L"", HELP_TEXT_ATTRIBUTES,HELP_DISPLAY_DETAILS_TEXT, FALSE, ValueRequired}, {UNITS_OPTION_SHORT, UNITS_OPTION, L"", UNITS_OPTION_HELP,HELP_UNIT_DETAILS_TEXT, FALSE, ValueRequired} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, { //!< targets {SYSTEM_TARGET, L"", L"", TRUE, ValueEmpty}, {CAPABILITIES_TARGET, L"", L"", TRUE, ValueEmpty} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show the platform supported " PMEM_MODULE_STR L" capabilities.", ShowSystemCapabilities, TRUE }; CHAR16 *mppAllowedShowSystemCapabilitiesDisplayValues[] = { PLATFORM_CONFIG_SUPPORT_STR, MEMORY_ALIGNMENT_STR, VOLATILE_MODE_ALLOWED_STR, VOLATILE_MODE_CURRENT_STR, APPDIRECT_MODE_ALLOWED_STR, OPERATING_MODE_SUPPORT_STR, APPDIRECT_SETTINGS_SUPPORTED_STR, APPDIRECT_SETTINGS_RECOMMENDED_STR, MIN_NAMESPACE_SIZE_STR, APPDIRECT_MIRROR_SUPPORTED_STR, DIMM_SPARE_SUPPORTED_STR, APPDIRECT_MIGRATION_SUPPORTED_STR, RENAME_NAMESPACE_SUPPORTED_STR, GROW_APPDIRECT_NAMESPACE_SUPPORTED_STR, SHRINK_APPDIRECT_NAMESPACE_SUPPORTED_STR, INITIATE_SCRUB_SUPPORTED, ASYNCHRONOUS_DRAM_REFRESH_SUPPORTED_STR, ERASE_DEVICE_DATA_SUPPORTED_STR, ENABLE_DEVICE_SECURITY_SUPPORTED_STR, DISABLE_DEVICE_SECURITY_SUPPORTED_STR, UNLOCK_DEVICE_SECURITY_SUPPORTED_STR, FREEZE_DEVICE_SECURITY_SUPPORTED_STR, CHANGE_DEVICE_PASSPHRASE_SUPPORTED_STR, CHANGE_MASTER_PASSPHRASE_SUPPORTED_STR, MASTER_ERASE_DEVICE_DATA_SUPPORTED_STR }; /** Prints allowed volatile mode @param[in] MemoryMode Byte describing allowed memory mode **/ STATIC VOID PrintAllowedVolatileMode( IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath, IN CURRENT_MEMORY_MODE MemoryMode ) { switch (MemoryMode.MemoryModeSplit.AllowedVolatileMode) { case VOLATILE_MODE_1LM: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, VOLATILE_MODE_ALLOWED_STR, ONE_LM_STR); break; case VOLATILE_MODE_1LM_OR_2LM: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, VOLATILE_MODE_ALLOWED_STR, ONE_LM_OR_TWO_LM_STR); break; case VOLATILE_MODE_1LM_PLUS_2LM: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, VOLATILE_MODE_ALLOWED_STR, ONE_LM_PLUS_TWO_LM_STR); break; default: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, VOLATILE_MODE_ALLOWED_STR, UNKNOWN_STR); break; } } /** Prints current volatile mode @param[in] MemoryMode Byte describing current memory mode **/ STATIC VOID PrintCurrentVolatileMode( IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath, IN CURRENT_MEMORY_MODE MemoryMode ) { switch (MemoryMode.MemoryModeSplit.CurrentVolatileMode) { case VOLATILE_MODE_1LM: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, VOLATILE_MODE_CURRENT_STR, ONE_LM_STR); break; case VOLATILE_MODE_2LM: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, VOLATILE_MODE_CURRENT_STR, TWO_LM_STR); break; case VOLATILE_MODE_1LM_PLUS_2LM: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, VOLATILE_MODE_CURRENT_STR, ONE_LM_PLUS_TWO_LM_STR); break; default: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, VOLATILE_MODE_CURRENT_STR, UNKNOWN_STR); break; } } /** Prints allowed appdirect mode @param[in] MemoryMode Byte describing allowed memory mode **/ STATIC VOID PrintAllowedAppDirectMode( IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath, IN CURRENT_MEMORY_MODE MemoryMode ) { switch (MemoryMode.MemoryModeSplit.PersistentMode) { case PERSISTENT_MODE_DISABLED: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, APPDIRECT_MODE_ALLOWED_STR, DISABLED_STR); break; case PERSISTENT_MODE_APP_DIRECT: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, APPDIRECT_MODE_ALLOWED_STR, APPDIRECT_STR); break; default: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, APPDIRECT_MODE_ALLOWED_STR, UNKNOWN_STR); break; } } /** Prints supported memory modes @param[in] MemoryModes Byte describing supported memory modes **/ STATIC VOID PrintSupportedMemoryModes( IN PRINT_CONTEXT *pPrinterCtx, IN CHAR16 *pPath, IN SUPPORTED_MEMORY_MODE MemoryModes ) { BOOLEAN First = TRUE; CHAR16 *Val = NULL; if (MemoryModes.MemoryModesFlags.OneLm) { Val = CatSPrint(Val, ONE_LM_STR); First = FALSE; } if (MemoryModes.MemoryModesFlags.Memory) { if (First) { First = FALSE; } else { Val = CatSPrintClean(Val, L", "); } Val = CatSPrintClean(Val, TWO_LM_STR); } if (MemoryModes.MemoryModesFlags.AppDirect) { if (First) { First = FALSE; } else { Val = CatSPrintClean(Val, L", "); } Val = CatSPrintClean(Val, APPDIRECT_STR); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, OPERATING_MODE_SUPPORT_STR, Val); FREE_POOL_SAFE(Val); } /** Execute the show system capabilities command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS ShowSystemCapabilities( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TempReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; SYSTEM_CAPABILITIES_INFO SystemCapabilitiesInfo; BOOLEAN FilterOutput = FALSE; BOOLEAN ShowAll = FALSE; CHAR16 *pDisplayValues = NULL; CHAR16 *pCapacityStr = NULL; UINT16 UnitsOption = DISPLAY_SIZE_UNIT_UNKNOWN; UINT16 UnitsToDisplay = FixedPcdGet16(PcdDcpmmCliDefaultCapacityUnit); CURRENT_MEMORY_MODE TempCurrentMode; SUPPORTED_MEMORY_MODE TempSupportedMode; DISPLAY_PREFERENCES DisplayPreferences; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; CHAR16 *TempAppDirSettings = NULL; NVDIMM_ENTRY(); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); SetMem(&SystemCapabilitiesInfo, sizeof(SystemCapabilitiesInfo), 0x0); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } UnitsToDisplay = DisplayPreferences.SizeUnit; ReturnCode = GetUnitsOption(pCmd, &UnitsOption); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Any valid units option will override the preferences **/ if (UnitsOption != DISPLAY_SIZE_UNIT_UNKNOWN) { UnitsToDisplay = UnitsOption; } /** Make sure we can access the config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetSystemCapabilitiesInfo(pNvmDimmConfigProtocol, &SystemCapabilitiesInfo); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } pDisplayValues = getOptionValue(pCmd, DISPLAY_OPTION); if (pDisplayValues) { FilterOutput = TRUE; } else { pDisplayValues = getOptionValue(pCmd, DISPLAY_OPTION_SHORT); if (pDisplayValues) { FilterOutput = TRUE; } } if (FilterOutput) { ReturnCode = CheckDisplayList(pDisplayValues, mppAllowedShowSystemCapabilitiesDisplayValues, ALLOWED_DISP_VALUES_COUNT(mppAllowedShowSystemCapabilitiesDisplayValues)); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_OPTION_DISPLAY); goto Finish; } } ShowAll = (containsOption(pCmd, ALL_OPTION) || containsOption(pCmd, ALL_OPTION_SHORT)); if (FilterOutput && ShowAll) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPTIONS_ALL_DISPLAY_USED_TOGETHER); goto Finish; } PRINTER_BUILD_KEY_PATH(pPath, DS_ROOT_PATH); /** Values shown by default **/ if (FilterOutput == ContainsValue(pDisplayValues, PLATFORM_CONFIG_SUPPORT_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PLATFORM_CONFIG_SUPPORT_STR, FORMAT_INT32, BIT_GET(SystemCapabilitiesInfo.PlatformConfigSupported, PLATFORM_CONFIG_SUPPORTED_BIT)); } if (FilterOutput == ContainsValue(pDisplayValues, MEMORY_ALIGNMENT_STR)) { TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, Pow(2, SystemCapabilitiesInfo.InterleaveAlignmentSize), UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEMORY_ALIGNMENT_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); } TempCurrentMode.MemoryMode = SystemCapabilitiesInfo.CurrentOperatingMode; if (FilterOutput == ContainsValue(pDisplayValues, VOLATILE_MODE_ALLOWED_STR)) { PrintAllowedVolatileMode(pPrinterCtx, pPath, TempCurrentMode); } if (FilterOutput == ContainsValue(pDisplayValues, VOLATILE_MODE_CURRENT_STR)) { PrintCurrentVolatileMode(pPrinterCtx, pPath, TempCurrentMode); } if (FilterOutput == ContainsValue(pDisplayValues, APPDIRECT_MODE_ALLOWED_STR)) { PrintAllowedAppDirectMode(pPrinterCtx, pPath, TempCurrentMode); } /** Values shown when -d/-a option specified **/ if (ShowAll || ContainsValue(pDisplayValues, OPERATING_MODE_SUPPORT_STR)) { TempSupportedMode.MemoryModes = SystemCapabilitiesInfo.OperatingModeSupport; PrintSupportedMemoryModes(pPrinterCtx, pPath, TempSupportedMode); } if (ShowAll || ContainsValue(pDisplayValues, MIN_NAMESPACE_SIZE_STR)) { TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, SystemCapabilitiesInfo.MinNsSize, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MIN_NAMESPACE_SIZE_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); } if (ShowAll || ContainsValue(pDisplayValues, APPDIRECT_MIRROR_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, APPDIRECT_MIRROR_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.AppDirectMirrorSupported); } if (ShowAll || ContainsValue(pDisplayValues, DIMM_SPARE_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DIMM_SPARE_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.DimmSpareSupported); } if (ShowAll || ContainsValue(pDisplayValues, APPDIRECT_MIGRATION_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, APPDIRECT_MIGRATION_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.AppDirectMigrationSupported); } if (ShowAll || ContainsValue(pDisplayValues, RENAME_NAMESPACE_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, RENAME_NAMESPACE_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.RenameNsSupported); } if (ShowAll || ContainsValue(pDisplayValues, GROW_APPDIRECT_NAMESPACE_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, GROW_APPDIRECT_NAMESPACE_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.GrowPmNsSupported); } if (ShowAll || ContainsValue(pDisplayValues, SHRINK_APPDIRECT_NAMESPACE_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SHRINK_APPDIRECT_NAMESPACE_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.ShrinkPmNsSupported); } if (ShowAll || ContainsValue(pDisplayValues, INITIATE_SCRUB_SUPPORTED)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, INITIATE_SCRUB_SUPPORTED, FORMAT_INT32, SystemCapabilitiesInfo.InitiateScrubSupported); } if (ShowAll || ContainsValue(pDisplayValues, ASYNCHRONOUS_DRAM_REFRESH_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, ASYNCHRONOUS_DRAM_REFRESH_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.AdrSupported); } if (ShowAll || ContainsValue(pDisplayValues, ERASE_DEVICE_DATA_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, ERASE_DEVICE_DATA_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.EraseDeviceDataSupported); } if (ShowAll || ContainsValue(pDisplayValues, ENABLE_DEVICE_SECURITY_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, ENABLE_DEVICE_SECURITY_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.EnableDeviceSecuritySupported); } if (ShowAll || ContainsValue(pDisplayValues, DISABLE_DEVICE_SECURITY_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DISABLE_DEVICE_SECURITY_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.DisableDeviceSecuritySupported); // supported for both OS and UEFI } if (ShowAll || ContainsValue(pDisplayValues, UNLOCK_DEVICE_SECURITY_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, UNLOCK_DEVICE_SECURITY_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.UnlockDeviceSecuritySupported); } if (ShowAll || ContainsValue(pDisplayValues, FREEZE_DEVICE_SECURITY_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, FREEZE_DEVICE_SECURITY_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.FreezeDeviceSecuritySupported); } if (ShowAll || ContainsValue(pDisplayValues, CHANGE_DEVICE_PASSPHRASE_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, CHANGE_DEVICE_PASSPHRASE_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.ChangeDevicePassphraseSupported); } if (ShowAll || ContainsValue(pDisplayValues, CHANGE_MASTER_PASSPHRASE_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, CHANGE_MASTER_PASSPHRASE_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.ChangeMasterPassphraseSupported); } if (ShowAll || ContainsValue(pDisplayValues, MASTER_ERASE_DEVICE_DATA_SUPPORTED_STR)) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MASTER_ERASE_DEVICE_DATA_SUPPORTED_STR, FORMAT_INT32, SystemCapabilitiesInfo.MasterEraseDeviceDataSupported); } if (ShowAll || ContainsValue(pDisplayValues, APPDIRECT_SETTINGS_SUPPORTED_STR)) { TempAppDirSettings = PrintAppDirectSettings( (VOID *)SystemCapabilitiesInfo.PtrInterleaveFormatsSupported, SystemCapabilitiesInfo.InterleaveFormatsSupportedNum, (INTERLEAVE_SIZE *)SystemCapabilitiesInfo.PtrInterleaveSize, FALSE, PRINT_SETTINGS_FORMAT_FOR_SHOW_SYS_CAP_CMD); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, APPDIRECT_SETTINGS_SUPPORTED_STR, TempAppDirSettings); FREE_POOL_SAFE(TempAppDirSettings); } if (ShowAll || ContainsValue(pDisplayValues, APPDIRECT_SETTINGS_RECOMMENDED_STR)) { TempAppDirSettings = PrintAppDirectSettings( (VOID *)SystemCapabilitiesInfo.PtrInterleaveFormatsSupported, SystemCapabilitiesInfo.InterleaveFormatsSupportedNum, (INTERLEAVE_SIZE *)SystemCapabilitiesInfo.PtrInterleaveSize, TRUE, PRINT_SETTINGS_FORMAT_FOR_SHOW_SYS_CAP_CMD); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, APPDIRECT_SETTINGS_RECOMMENDED_STR, TempAppDirSettings); FREE_POOL_SAFE(TempAppDirSettings); } Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_HII_POINTER(SystemCapabilitiesInfo.PtrInterleaveFormatsSupported); FREE_HII_POINTER(SystemCapabilitiesInfo.PtrInterleaveSize); FREE_POOL_SAFE(pDisplayValues); FREE_POOL_SAFE(pPath); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the show system capabilities command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowSystemCapabilitiesCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&ShowSystemCapabilitiesCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowSystemCapabilitiesCommand.h000066400000000000000000000054031440615110200240200ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SRC_CLI_SHOW_SYSTEM_CAPABILITIES_COMMAND_H_ #define _SRC_CLI_SHOW_SYSTEM_CAPABILITIES_COMMAND_H_ #include "CommandParser.h" #include /** Display options for this command **/ #define PLATFORM_CONFIG_SUPPORT_STR L"PlatformConfigSupported" #define MEMORY_ALIGNMENT_STR L"Alignment" #define VOLATILE_MODE_ALLOWED_STR L"AllowedVolatileMode" #define VOLATILE_MODE_CURRENT_STR L"CurrentVolatileMode" #define APPDIRECT_MODE_ALLOWED_STR L"AllowedAppDirectMode" #define OPERATING_MODE_SUPPORT_STR L"ModesSupported" #define APPDIRECT_SETTINGS_SUPPORTED_STR L"SupportedAppDirectSettings" #define APPDIRECT_SETTINGS_RECOMMENDED_STR L"RecommendedAppDirectSettings" #define MIN_NAMESPACE_SIZE_STR L"MinNamespaceSize" #define APPDIRECT_MIRROR_SUPPORTED_STR L"AppDirectMirrorSupported" #define DIMM_SPARE_SUPPORTED_STR L"DimmSpareSupported" #define APPDIRECT_MIGRATION_SUPPORTED_STR L"AppDirectMigrationSupported" #define RENAME_NAMESPACE_SUPPORTED_STR L"RenameNamespaceSupported" #define GROW_APPDIRECT_NAMESPACE_SUPPORTED_STR L"GrowAppDirectNamespaceSupported" #define SHRINK_APPDIRECT_NAMESPACE_SUPPORTED_STR L"ShrinkAppDirectNamespaceSupported" #define INITIATE_SCRUB_SUPPORTED L"InitiateScrubSupported" #define ASYNCHRONOUS_DRAM_REFRESH_SUPPORTED_STR L"AdrSupported" #define ERASE_DEVICE_DATA_SUPPORTED_STR L"EraseDeviceDataSupported" #define ENABLE_DEVICE_SECURITY_SUPPORTED_STR L"EnableDeviceSecuritySupported" #define DISABLE_DEVICE_SECURITY_SUPPORTED_STR L"DisableDeviceSecuritySupported" #define UNLOCK_DEVICE_SECURITY_SUPPORTED_STR L"UnlockDeviceSecuritySupported" #define FREEZE_DEVICE_SECURITY_SUPPORTED_STR L"FreezeDeviceSecuritySupported" #define CHANGE_DEVICE_PASSPHRASE_SUPPORTED_STR L"ChangeDevicePassphraseSupported" #define CHANGE_MASTER_PASSPHRASE_SUPPORTED_STR L"ChangeMasterPassphraseSupported" #define MASTER_ERASE_DEVICE_DATA_SUPPORTED_STR L"MasterEraseDeviceDataSupported" #define APPDIRECT_STR L"App Direct" #define TWO_LM_STR L"2LM" #define ONE_LM_STR L"1LM" #define ONE_LM_OR_TWO_LM_STR L"1LM or 2LM" #define ONE_LM_PLUS_TWO_LM_STR L"1LM+2LM" #define DISABLED_STR L"Disabled" #define UNKNOWN_STR L"Unknown" EFI_STATUS ShowSystemCapabilities( IN struct Command *pCmd ); EFI_STATUS RegisterShowSystemCapabilitiesCommand( ); #endif /* _SRC_CLI_SHOW_SYSTEM_CAPABILITIES_COMMAND_H_ */ ipmctl-03.00.00.0485/DcpmPkg/cli/ShowTopologyCommand.c000066400000000000000000000617011440615110200220340ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include "ShowTopologyCommand.h" #include "ShowDimmsCommand.h" #include "NvmDimmCli.h" #include "Uefi.h" #include "Common.h" #include #include #include #define DS_ROOT_PATH L"/DimmTopologyList" #define DS_DIMM_TOPOLOGY_PATH L"/DimmTopologyList/DimmTopology" #define DS_DIMM_TOPOLOGY_INDEX_PATH L"/DimmTopologyList/DimmTopology[%d]" #define DS_DIMM_SOCKET_INDEX_PATH L"/DimmTopologyList/Socket[%d]" #define DS_DIMM_DIE_INDEX_PATH L"/DimmTopologyList/Socket[%d]/Die[%d]" #define DS_DIMM_IMC_INDEX_PATH L"/DimmTopologyList/Socket[%d]/Die[%d]/Imc[%d]" #define DS_DIMM_CHANNEL_INDEX_PATH L"/DimmTopologyList/Socket[%d]/Die[%d]/Imc[%d]/Channel[%d]" #define DS_DIMM_SLOT_INDEX_PATH L"/DimmTopologyList/Socket[%d]/Die[%d]/Imc[%d]/Channel[%d]/Slot[%d]" #define DS_DIMM_DIMM_INDEX_PATH L"/DimmTopologyList/Socket[%d]/Die[%d]/Imc[%d]/Channel[%d]/Slot[%d]/Dimm[%d]" /* * PRINT LIST ATTRIBUTES * ---DimmId=0x0001--- * MemoryType=DDR4 * Capacity=16.0 GiB * PhysicalID=0x0051 * ... */ PRINTER_LIST_ATTRIB ShowTopoListAttributesPmtt1 = { { { TOPOLOGY_NODE_STR, //GROUP LEVEL TYPE L"---" DIMM_ID_STR L"=$(" DIMM_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR DIMM_ID_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; PRINTER_LIST_ATTRIB ShowTopoListAttributesPmtt2 = { { { SOCKET_NODE_STR, //GROUP LEVEL TYPE L"---" SOCKET_ID_STR L"=$(" SOCKET_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR SOCKET_ID_STR //NULL or IGNORE KEY LIST (K1;K2) }, { DIE_NODE_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT L"---" DIE_ID_STR L"=$(" DIE_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR DIE_ID_STR //NULL or IGNORE KEY LIST (K1;K2) }, { IMC_NODE_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT SHOW_LIST_IDENT L"---" MEMORY_CONTROLLER_STR L"=$(" MEMORY_CONTROLLER_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR MEMORY_CONTROLLER_STR //NULL or IGNORE KEY LIST (K1;K2) }, { CHANNEL_NODE_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT L"---" CHANNEL_ID_STR L"=$(" CHANNEL_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR CHANNEL_ID_STR //NULL or IGNORE KEY LIST (K1;K2) }, { SLOT_NODE_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT L"---" CHANNEL_POS_STR L"=$(" CHANNEL_POS_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls=%ls", //NULL or KEY VAL FORMAT STR CHANNEL_POS_STR //NULL or IGNORE KEY LIST (K1;K2) }, { DIMM_NODE_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT L"---" DIMM_ID_STR L"=$(" DIMM_ID_STR L")---", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR L": " FORMAT_STR, //NULL or KEY VAL FORMAT STR, DIMM_ID_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; /* * PRINTER TABLE ATTRIBUTES (5 columns) * DimmID | MemoryType | Capacity | PhysicalID | DeviceLocator * ========================================================== * 0x0001 | X | X | X | X * ... */ PRINTER_TABLE_ATTRIB ShowTopoTableAttributes = { { { DIMM_ID_STR, //COLUMN HEADER DIMM_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_TOPOLOGY_PATH PATH_KEY_DELIM DIMM_ID_STR //COLUMN DATA PATH }, { MEMORY_TYPE_STR, //COLUMN HEADER MEMORY_TYPE_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_TOPOLOGY_PATH PATH_KEY_DELIM MEMORY_TYPE_STR //COLUMN DATA PATH }, { CAPACITY_STR, //COLUMN HEADER CAPACITY_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_TOPOLOGY_PATH PATH_KEY_DELIM CAPACITY_STR //COLUMN DATA PATH }, { PHYSICAL_ID_STR, //COLUMN HEADER ID_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_TOPOLOGY_PATH PATH_KEY_DELIM PHYSICAL_ID_STR //COLUMN DATA PATH }, { DEVICE_LOCATOR_STR, //COLUMN HEADER DEVICE_LOCATOR_MAX_STR_WIDTH, //COLUMN MAX STR WIDTH DS_DIMM_TOPOLOGY_PATH PATH_KEY_DELIM DEVICE_LOCATOR_STR //COLUMN DATA PATH } } }; PRINTER_DATA_SET_ATTRIBS ShowTopoDataSetAttribsPmtt1 = { &ShowTopoListAttributesPmtt1, &ShowTopoTableAttributes }; PRINTER_DATA_SET_ATTRIBS ShowTopoDataSetAttribsPmtt2 = { &ShowTopoListAttributesPmtt2, &ShowTopoTableAttributes }; /** Command syntax definition **/ struct Command ShowTopologyCommand = { SHOW_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"", HELP_VERBOSE_DETAILS_TEXT , FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {ALL_OPTION_SHORT, ALL_OPTION, L"", L"", HELP_ALL_DETAILS_TEXT, FALSE, ValueEmpty}, {UNITS_OPTION_SHORT, UNITS_OPTION, L"", UNITS_OPTION_HELP, HELP_UNIT_DETAILS_TEXT, FALSE, ValueRequired} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, { //!< targets {TOPOLOGY_TARGET, L"", L"", TRUE, ValueEmpty}, {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional}, {SOCKET_TARGET, L"", HELP_TEXT_SOCKET_IDS, FALSE, ValueOptional} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Show the topology of all the DDRs and " PMEM_MODULES_STR L".", //!< help ShowTopology, TRUE }; CHAR16 *mppAllowedShowTopologyDisplayValues[] = {0}; /** Register the command **/ EFI_STATUS RegisterShowTopologyCommand( ) { EFI_STATUS Rc = EFI_SUCCESS; NVDIMM_ENTRY(); Rc = RegisterCommand(&ShowTopologyCommand); NVDIMM_EXIT_I64(Rc); return Rc; } /** Execute the command **/ EFI_STATUS ShowTopology( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_STATUS TempReturnCode = EFI_INVALID_PARAMETER; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; UINT16 *pSockets = NULL; UINT32 SocketsNum = 0; UINT16 *pDimmIds = NULL; UINT32 DimmIdsNum = 0; UINT32 DimmCount = 0; UINT16 Index = 0; UINT16 Index2 = 0; UINT16 IndexPmem = 0; UINT16 TopologyDimmsNumber = 0; UINT16 UnitsOption = DISPLAY_SIZE_UNIT_UNKNOWN; UINT16 UnitsToDisplay = FixedPcdGet16(PcdDcpmmCliDefaultCapacityUnit); BOOLEAN AllOptionSet = FALSE; BOOLEAN Found = FALSE; CHAR16 *pSocketsValue = NULL; CHAR16 *pDimmsValue = NULL; CHAR16 *pMemoryType = NULL; CHAR16 *pTempString = NULL; CHAR16 *pCapacityStr = NULL; DIMM_INFO *pDimms = NULL; TOPOLOGY_DIMM_INFO *pTopologyDimms = NULL; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; DISPLAY_PREFERENCES DisplayPreferences; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; CMD_DISPLAY_OPTIONS *pDispOptions = NULL; UINT32 TopoCnt = 0; BOOLEAN volatile DimmIsOkToDisplay[MAX_DIMMS]; UINT16 SocketID = MAX_UINT16; UINT16 DieID = MAX_UINT16; UINT16 MemControllerID = MAX_UINT16; UINT16 ChannelID = MAX_UINT16; UINT16 SlotID = MAX_UINT16; ACPI_REVISION Revision; NVDIMM_ENTRY(); ZeroMem(DimmStr, sizeof(DimmStr)); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; for (Index = 0; Index < MAX_DIMMS; Index++) { DimmIsOkToDisplay[Index] = FALSE; } pDispOptions = AllocateZeroPool(sizeof(CMD_DISPLAY_OPTIONS)); if (NULL == pDispOptions) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = CheckAllAndDisplayOptions(pCmd, mppAllowedShowTopologyDisplayValues, ALLOWED_DISP_VALUES_COUNT(mppAllowedShowTopologyDisplayValues), pDispOptions); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CheckAllAndDisplayOptions has returned error. Code " FORMAT_EFI_STATUS "\n", ReturnCode); goto Finish; } if (ContainTarget(pCmd, SOCKET_TARGET)) { pSocketsValue = GetTargetValue(pCmd, SOCKET_TARGET); ReturnCode = GetUintsFromString(pSocketsValue, &pSockets, &SocketsNum); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INCORRECT_VALUE_TARGET_SOCKET); goto Finish; } } AllOptionSet = pDispOptions->AllOptionSet; ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } UnitsToDisplay = DisplayPreferences.SizeUnit; ReturnCode = GetUnitsOption(pCmd, &UnitsOption); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Any valid units option will override the preferences **/ if (UnitsOption != DISPLAY_SIZE_UNIT_UNKNOWN) { UnitsToDisplay = UnitsOption; } /** make sure we can access the config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Populate the list of DIMM_INFO structures with relevant information ReturnCode = GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode) || (pDimms == NULL)) { goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetSystemTopology(pNvmDimmConfigProtocol, &pTopologyDimms, &TopologyDimmsNumber); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } /** Check if proper -dimm target is given **/ if (ContainTarget(pCmd, DIMM_TARGET)) { pDimmsValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pDimmsValue, pDimms, DimmCount, &pDimmIds, &DimmIdsNum); if (EFI_ERROR(ReturnCode)) { goto Finish; } /*Mark each dimm as ok to display based on the dimms passed by the user*/ for (Index = 0; Index < DimmCount; Index++) { for (Index2 = 0; Index2 < DimmIdsNum; Index2++) { if (pDimms[Index].DimmID == pDimmIds[Index2]) { DimmIsOkToDisplay[Index] = TRUE; } } } } else { /*Since no dimms were specified, mark them all as ok to display*/ for (Index = 0; Index < MAX_DIMMS; Index++) { DimmIsOkToDisplay[Index] = TRUE; } } /** Check if proper -socket target is given **/ for (Index = 0; Index < SocketsNum; Index++) { Found = FALSE; /*Only display dimms which match the socket(s) specified *and* that the user has indicated*/ for (Index2 = 0; Index2 < DimmCount; Index2++) { if (DimmIsOkToDisplay[Index2] == TRUE && pSockets[Index] == pDimms[Index2].SocketId) { Found = TRUE; break; } } if (!Found) { ReturnCode = EFI_NOT_FOUND; if (DimmIdsNum > 0) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_SPECIFIED_DIMMS_ON_SPECIFIED_SOCKET); } else { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INVALID_SOCKET_ID); } NVDIMM_WARN("Invalid Socket ID"); goto Finish; } } Revision.AsUint8 = pTopologyDimms[Index].PmttVersion; /** display a summary table of all dimms **/ if (!AllOptionSet) { //Print topology for DDR entries if no dimm target specified for (Index = 0; Index < TopologyDimmsNumber; Index++) { if (SocketsNum > 0 && !ContainUint(pSockets, SocketsNum, pTopologyDimms[Index].SocketID)) { continue; } if (DimmIdsNum > 0 && !ContainUint(pDimmIds, DimmIdsNum, pDimms[IndexPmem].DimmID)) { IndexPmem++; continue; } if (pTopologyDimms[Index].MemoryType != MEMORYTYPE_DCPM && ContainTarget(pCmd, DIMM_TARGET)) { continue; } pMemoryType = MemoryTypeToStr(pTopologyDimms[Index].MemoryType); TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pTopologyDimms[Index].VolatileCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_TOPOLOGY_INDEX_PATH, TopoCnt); if (pTopologyDimms[Index].MemoryType == MEMORYTYPE_DCPM) { ReturnCode = GetPreferredDimmIdAsString(pTopologyDimms[Index].DimmHandle, pDimms[IndexPmem].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, DimmStr); IndexPmem++; } else { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, NOT_APPLICABLE_SHORT_STR); } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEMORY_TYPE_STR, pMemoryType); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, CAPACITY_STR, pCapacityStr); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PHYSICAL_ID_STR, FORMAT_HEX, pTopologyDimms[Index].DimmID); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DEVICE_LOCATOR_STR, pTopologyDimms[Index].DeviceLocator); ++TopoCnt; FREE_POOL_SAFE(pMemoryType); FREE_POOL_SAFE(pCapacityStr); } } /** display detailed view for PMTT 0.1 **/ // If we are here with an invalid PMTT, we are able to derive the topology from the SMBIOS table successfully. // Print in the style of a PMTT 0.1 table else if (IS_ACPI_REV_MAJ_0_MIN_1(Revision) || IS_PMTT_REVISION_INVALID(Revision)) { SetDisplayInfo(L"DimmTopology", ListView, NULL); //Print detailed topology for DDR entries if no dimm target specified for (Index = 0; Index < TopologyDimmsNumber; Index++) { if (SocketsNum > 0 && !ContainUint(pSockets, SocketsNum, pTopologyDimms[Index].SocketID)) { continue; } if (DimmIdsNum > 0 && !ContainUint(pDimmIds, DimmIdsNum, pDimms[IndexPmem].DimmID)) { IndexPmem++; continue; } if (pTopologyDimms[Index].MemoryType != MEMORYTYPE_DCPM && ContainTarget(pCmd, DIMM_TARGET)) { continue; } PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_TOPOLOGY_INDEX_PATH, TopoCnt); /** Always Print DimmIDs **/ if (pTopologyDimms[Index].MemoryType == MEMORYTYPE_DCPM) { ReturnCode = GetPreferredDimmIdAsString(pTopologyDimms[Index].DimmHandle, pDimms[IndexPmem].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, DimmStr); IndexPmem++; } else { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, NOT_APPLICABLE_SHORT_STR); } /** MemoryType **/ pTempString = MemoryTypeToStr(pTopologyDimms[Index].MemoryType); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEMORY_TYPE_STR, pTempString); FREE_POOL_SAFE(pTempString); /** Capacity **/ //Convert Megabytes to Gigabytes and get digits after point from number TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pTopologyDimms[Index].VolatileCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, CAPACITY_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); /** PhysicalID **/ PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PHYSICAL_ID_STR, FORMAT_HEX, pTopologyDimms[Index].DimmID); /** DeviceLocator **/ PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DEVICE_LOCATOR_STR, pTopologyDimms[Index].DeviceLocator); /** SocketID, MemControllerID, ChannelID, ChannelPos, NodeControllerID **/ if (pTopologyDimms[Index].MemoryType == MEMORYTYPE_DCPM) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SOCKET_ID_STR, FORMAT_HEX, pTopologyDimms[Index].SocketID); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MEMORY_CONTROLLER_STR, FORMAT_HEX, pTopologyDimms[Index].MemControllerID); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, CHANNEL_ID_STR, FORMAT_HEX, pTopologyDimms[Index].ChannelID); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, CHANNEL_POS_STR, FORMAT_INT32, pTopologyDimms[Index].SlotID); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, NODE_CONTROLLER_ID_STR, FORMAT_HEX, pTopologyDimms[Index].NodeControllerID); } else { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SOCKET_ID_STR, NOT_APPLICABLE_SHORT_STR); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEMORY_CONTROLLER_STR, NOT_APPLICABLE_SHORT_STR); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, CHANNEL_ID_STR, NOT_APPLICABLE_SHORT_STR); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, CHANNEL_POS_STR, NOT_APPLICABLE_SHORT_STR); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, NODE_CONTROLLER_ID_STR, NOT_APPLICABLE_SHORT_STR); } /** BankLabel **/ PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, BANK_LABEL_STR, pTopologyDimms[Index].BankLabel); ++TopoCnt; } } /** display detailed view for PMTT 0.2 **/ else if (IS_ACPI_REV_MAJ_0_MIN_2(Revision)) { SetDisplayInfo(L"DimmTopology", ListView, NULL); //Print detailed topology for DDR entries if no dimm target specified for (Index = 0; Index < TopologyDimmsNumber; Index++) { if (SocketsNum > 0 && !ContainUint(pSockets, SocketsNum, pTopologyDimms[Index].SocketID)) { continue; } if (DimmIdsNum > 0 && !ContainUint(pDimmIds, DimmIdsNum, pDimms[IndexPmem].DimmID)) { IndexPmem++; continue; } if (pTopologyDimms[Index].MemoryType != MEMORYTYPE_DCPM && ContainTarget(pCmd, DIMM_TARGET)) { continue; } /** SocketID **/ PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_SOCKET_INDEX_PATH, pTopologyDimms[Index].SocketID); SocketID = pTopologyDimms[Index].SocketID; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, SOCKET_ID_STR, FORMAT_HEX, pTopologyDimms[Index].SocketID); /** DieID **/ PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_DIE_INDEX_PATH, SocketID, pTopologyDimms[Index].DieID); DieID = pTopologyDimms[Index].DieID; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, DIE_ID_STR, FORMAT_HEX, pTopologyDimms[Index].DieID); /** MemControllerID **/ PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_IMC_INDEX_PATH, SocketID, DieID, pTopologyDimms[Index].MemControllerID); MemControllerID = pTopologyDimms[Index].MemControllerID; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, MEMORY_CONTROLLER_STR, FORMAT_HEX, pTopologyDimms[Index].MemControllerID); /** ChannelID **/ PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_CHANNEL_INDEX_PATH, SocketID, DieID, MemControllerID, pTopologyDimms[Index].ChannelID); ChannelID = pTopologyDimms[Index].ChannelID; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, CHANNEL_ID_STR, FORMAT_HEX, pTopologyDimms[Index].ChannelID); /** ChannelPos **/ PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_SLOT_INDEX_PATH, SocketID, DieID, MemControllerID, ChannelID, pTopologyDimms[Index].SlotID); SlotID = pTopologyDimms[Index].SlotID; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, CHANNEL_POS_STR, FORMAT_HEX, pTopologyDimms[Index].SlotID); PRINTER_BUILD_KEY_PATH(pPath, DS_DIMM_DIMM_INDEX_PATH, SocketID, DieID, MemControllerID, ChannelID, SlotID, TopoCnt); /** Always Print DimmIDs **/ if (pTopologyDimms[Index].MemoryType == MEMORYTYPE_DCPM) { ReturnCode = GetPreferredDimmIdAsString(pTopologyDimms[Index].DimmHandle, pDimms[IndexPmem].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, DimmStr); IndexPmem++; } else { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DIMM_ID_STR, NOT_APPLICABLE_SHORT_STR); } /** MemoryType **/ pTempString = MemoryTypeToStr(pTopologyDimms[Index].MemoryType); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MEMORY_TYPE_STR, pTempString); FREE_POOL_SAFE(pTempString); /** Capacity **/ //Convert Megabytes to Gigabytes and get digits after point from number TempReturnCode = MakeCapacityString(gNvmDimmCliHiiHandle, pTopologyDimms[Index].VolatileCapacity, UnitsToDisplay, TRUE, &pCapacityStr); KEEP_ERROR(ReturnCode, TempReturnCode); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, CAPACITY_STR, pCapacityStr); FREE_POOL_SAFE(pCapacityStr); /** PhysicalID **/ PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, PHYSICAL_ID_STR, FORMAT_HEX, pTopologyDimms[Index].DimmID); /** DeviceLocator **/ PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, DEVICE_LOCATOR_STR, pTopologyDimms[Index].DeviceLocator); /** NodeControllerID **/ PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, NODE_CONTROLLER_ID_STR, FORMAT_HEX, pTopologyDimms[Index].NodeControllerID); /** BankLabel **/ PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, BANK_LABEL_STR, pTopologyDimms[Index].BankLabel); ++TopoCnt; } } //Specify table attributes if (IS_ACPI_REV_MAJ_0_MIN_1(Revision)) { PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowTopoDataSetAttribsPmtt1); } else { PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &ShowTopoDataSetAttribsPmtt2); } Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FREE_POOL_SAFE(pPath); FREE_CMD_DISPLAY_OPTIONS_SAFE(pDispOptions); FREE_POOL_SAFE(pDimms); FREE_POOL_SAFE(pMemoryType); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pSockets); FREE_POOL_SAFE(pTopologyDimms); FREE_POOL_SAFE(pCapacityStr); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/ShowTopologyCommand.h000066400000000000000000000017731440615110200220440ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_TOPOLOGY_COMMAND_H_ #define _SHOW_TOPOLOGY_COMMAND_H_ #include "Uefi.h" #include "CommandParser.h" /** Register the show topology command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterShowTopologyCommand( ); /** show -dimm display options some of common display options are in CommandParser.h or in ShowDimmsCommand.h **/ #define NODE_CONTROLLER_ID_STR L"NodeControllerID" /** Execute the show topology command @param[in] pCmd command from CLI @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL function failure **/ EFI_STATUS ShowTopology( IN struct Command *pCmd ); #endif /** _SHOW_TOPOLOGY_COMMAND_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/StartDiagnosticCommand.c000066400000000000000000000267361440615110200224720ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include "Debug.h" #include "Utility.h" #include "Common.h" #include "NvmInterface.h" #include "StartDiagnosticCommand.h" #include #include #include #include #define DS_ROOT_PATH L"/DiagnosticList" #define DS_DIAGNOSTIC_PATH L"/DiagnosticList/Diagnostic" #define DS_DIAGNOSTIC_INDEX_PATH L"/DiagnosticList/Diagnostic[%d]" #define DS_SUBTEST_PATH L"/DiagnosticList/Diagnostic/SubTest" #define DS_SUBTEST_INDEX_PATH L"/DiagnosticList/Diagnostic[%d]/SubTest[%d]" #define TEST_NAME_STR L"Test" #define SUBTEST_NAME_STR L"SubTest" #define RESULT_STR L"RESULT" #define DIAG_ENTRY_EOL L'\n' /* * PRINT LIST ATTRIBUTES * --Test = Quick * Message = The quick health check succeeded * State = Ok * Result Code = 0 * --SubTest = PCD * State = ok * Message.1 = X * Event Code.1 = X */ PRINTER_LIST_ATTRIB StartDiagListAttributes = { { { DIAGNOSTIC_NODE_STR, //GROUP LEVEL TYPE L"\n--" TEST_NAME_STR L" = $(" TEST_NAME_STR L")", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT L"%ls = %ls", //NULL or KEY VAL FORMAT STR TEST_NAME_STR //NULL or IGNORE KEY LIST (K1;K2) }, { SUBTEST_NAME_STR, //GROUP LEVEL TYPE SHOW_LIST_IDENT L"--" SUBTEST_NAME_STR L" = $(" SUBTEST_NAME_STR L")", //NULL or GROUP LEVEL HEADER SHOW_LIST_IDENT SHOW_LIST_IDENT L"%ls = %ls", //NULL or KEY VAL FORMAT STR SUBTEST_NAME_STR //NULL or IGNORE KEY LIST (K1;K2) } } }; PRINTER_DATA_SET_ATTRIBS StartDiagDataSetAttribs = { &StartDiagListAttributes, NULL }; /** Command syntax definition **/ COMMAND StartDiagnosticCommand = { START_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", LARGE_PAYLOAD_OPTION, L"", L"", HELP_LPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", SMALL_PAYLOAD_OPTION, L"", L"", HELP_SPAYLOAD_DETAILS_TEXT, FALSE, ValueEmpty} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #else ,{L"", L"", L"", L"", L"",FALSE, ValueOptional} #endif }, { //!< targets {DIAGNOSTIC_TARGET, L"", ALL_DIAGNOSTICS_TARGETS, TRUE, ValueOptional}, {DIMM_TARGET, L"", HELP_TEXT_DIMM_IDS, FALSE, ValueOptional}, }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Run a diagnostic test on one or more " PMEM_MODULES_STR L".", //!< help StartDiagnosticCmd, TRUE }; /** Get Diagnostic types to be executed from COMMAND @param[in] pCmd command from CLI @retval One of DIAGNOSTIC_TEST_X types @retval DIAGNOSTIC_TEST_UNKNOWN if any error occurs **/ UINT8 GetDiagnosticTestType( IN COMMAND *pCmd ) { CHAR16 *pDiagnosticTargetValue = NULL; CHAR16 **ppStringElements = NULL; UINT16 Index = 0; UINT8 ChosenDiagnosticTests = DIAGNOSTIC_TEST_UNKNOWN; UINT32 ElementsCount = 0; if (pCmd == NULL) { goto Finish; } if (!ContainTarget(pCmd, DIAGNOSTIC_TARGET)) { goto Finish; } pDiagnosticTargetValue = GetTargetValue(pCmd, DIAGNOSTIC_TARGET); ppStringElements = StrSplit(pDiagnosticTargetValue, L',', &ElementsCount); if (ppStringElements == NULL) { /** If no diagnostic test was given explicitly, start all test**/ ChosenDiagnosticTests |= DIAGNOSTIC_TEST_ALL; goto Finish; } for (Index = 0; Index < ElementsCount; ++Index) { if (StrICmp(ppStringElements[Index], QUICK_TEST_TARGET_VALUE) == 0) { ChosenDiagnosticTests |= DIAGNOSTIC_TEST_QUICK; } else if (StrICmp(ppStringElements[Index], CONFIG_TEST_TARGET_VALUE) == 0) { ChosenDiagnosticTests |= DIAGNOSTIC_TEST_CONFIG; } else if (StrICmp(ppStringElements[Index], SECURITY_TEST_TARGET_VALUE) == 0) { ChosenDiagnosticTests |= DIAGNOSTIC_TEST_SECURITY; } else if (StrICmp(ppStringElements[Index], FW_TEST_TARGET_VALUE) == 0) { ChosenDiagnosticTests |= DIAGNOSTIC_TEST_FW; } else { ChosenDiagnosticTests = DIAGNOSTIC_TEST_UNKNOWN; goto Finish; } } Finish: FreeStringArray(ppStringElements, ElementsCount); return ChosenDiagnosticTests; } /** Execute the Start Diagnostic command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS StartDiagnosticCmd( IN COMMAND *pCmd ) { EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT16 *pDimmIds = NULL; UINT32 DimmIdsCount = 0; CHAR16 *pDimmTargetValue = NULL; UINT8 ChosenDiagTests = DIAGNOSTIC_TEST_UNKNOWN; UINT8 CurrentDiagTest = DIAGNOSTIC_TEST_UNKNOWN; UINT32 Index = 0; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; DISPLAY_PREFERENCES DisplayPreferences; UINT8 DimmIdPreference = DISPLAY_DIMM_ID_HANDLE; DIAG_INFO *pFinalDiagnosticsResult = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pPath = NULL; UINT8 Id = 0; CHAR16 *MsgStr = NULL; CHAR16 *EventCodeStr = NULL; CHAR16 **ppSplitDiagResultLines = NULL; CHAR16 **ppSplitDiagEventCode = NULL; UINT32 NumTokens = 0; UINT32 CodeTokens = 0; UINT32 i = 0; CHAR16** EventMesg = NULL; NVDIMM_ENTRY(); SetDisplayInfo(L"Diagnostic", DiagView, NULL); ZeroMem(&DisplayPreferences, sizeof(DisplayPreferences)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; ReturnCode = ReadRunTimePreferences(&DisplayPreferences, DISPLAY_CLI_INFO); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_DISPLAY_PREFERENCES_RETRIEVE); goto Finish; } DimmIdPreference = DisplayPreferences.DimmIdentifier; ChosenDiagTests = GetDiagnosticTestType(pCmd); if (ChosenDiagTests == DIAGNOSTIC_TEST_UNKNOWN || ((ChosenDiagTests & DIAGNOSTIC_TEST_ALL) != ChosenDiagTests)) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_WRONG_DIAGNOSTIC_TARGETS, ALL_DIAGNOSTICS_TARGETS); goto Finish; } // NvmDimmConfigProtocol required ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // Retrieve basic information for *all* dimms on platform CHECK_RESULT(GetAllDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount), Finish); // check targets if (ContainTarget(pCmd, DIMM_TARGET)) { pDimmTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pDimmTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsCount); if (EFI_ERROR(ReturnCode)) { goto Finish; } } for (Index = 0; Index < DIAGNOSTIC_TEST_COUNT; ++Index) { CurrentDiagTest = (ChosenDiagTests & (1 << Index)); if (CurrentDiagTest == 0) { /** Test is not selected, skip **/ continue; } ReturnCode = pNvmDimmConfigProtocol->StartDiagnostic( pNvmDimmConfigProtocol, pDimmIds, DimmIdsCount, CurrentDiagTest, DimmIdPreference, &pFinalDiagnosticsResult); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Diagnostics failed"); } DIAG_INFO *pLoc = pFinalDiagnosticsResult; PRINTER_BUILD_KEY_PATH(pPath, DS_DIAGNOSTIC_INDEX_PATH, Index); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, TEST_NAME_STR, pLoc->TestName); if (pLoc->State != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"State", pLoc->State); } EventMesg = StrSplit(pLoc->Message, DIAG_ENTRY_EOL, &i); if(EventMesg != NULL){ PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath,L"Message" ,EventMesg[0]); FREE_POOL_SAFE(EventMesg); } for (Id = 0; Id < MAX_NO_OF_DIAGNOSTIC_SUBTESTS; Id++) { if (pLoc->SubTestName[Id] != NULL) { PRINTER_BUILD_KEY_PATH(pPath, DS_SUBTEST_INDEX_PATH, Index, Id); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SUBTEST_NAME_STR, pLoc->SubTestName[Id]); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, L"State", pLoc->SubTestState[Id]); // Split message string and set printer in unique key-> value form. if (pLoc->SubTestMessage[Id] != NULL) { ppSplitDiagResultLines = StrSplit(pLoc->SubTestMessage[Id], DIAG_ENTRY_EOL, &NumTokens); if (pLoc->SubTestEventCode[Id] != NULL) { ppSplitDiagEventCode = StrSplit(pLoc->SubTestEventCode[Id], DIAG_ENTRY_EOL, &CodeTokens); } for (i = 0; i < NumTokens; i++) { if (ppSplitDiagResultLines != NULL) { MsgStr = CatSPrint(NULL, L"Message.%d", i + 1); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, MsgStr, ppSplitDiagResultLines[i]); } if (ppSplitDiagEventCode != NULL) { EventCodeStr = CatSPrint(NULL, L"EventCode.%d", i + 1); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, EventCodeStr, ppSplitDiagEventCode[i]); } FREE_POOL_SAFE(MsgStr); FREE_POOL_SAFE(EventCodeStr); } FreeStringArray(ppSplitDiagResultLines, NumTokens); if (ppSplitDiagEventCode != NULL) { FreeStringArray(ppSplitDiagEventCode, CodeTokens); } } FREE_POOL_SAFE(pLoc->SubTestName[Id]); FREE_POOL_SAFE(pLoc->SubTestMessage[Id]); FREE_POOL_SAFE(pLoc->SubTestState[Id]); FREE_POOL_SAFE(pLoc->SubTestEventCode[Id]); } } FREE_POOL_SAFE(pLoc->TestName); FREE_POOL_SAFE(pLoc->Message); FREE_POOL_SAFE(pLoc->State); FREE_POOL_SAFE(pFinalDiagnosticsResult); } PRINTER_CONFIGURE_DATA_ATTRIBUTES(pPrinterCtx, DS_ROOT_PATH, &StartDiagDataSetAttribs); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); // free all memory structures FREE_POOL_SAFE(pPath); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the start diagnostic command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterStartDiagnosticCommand( ) { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&StartDiagnosticCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/StartDiagnosticCommand.h000066400000000000000000000014141440615110200224610ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _START_DIAGNOSTIC_COMMAND_H_ #define _START_DIAGNOSTIC_COMMAND_H_ #include /** Register the Start Diagnostic command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterStartDiagnosticCommand( ); /** Execute the Start Diagnostic command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS StartDiagnosticCmd( IN struct Command *pCmd ); #endif /** _START_DIAGNOSTIC_COMMAND_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/cli/StartFormatCommand.c000066400000000000000000000162151440615110200216250ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include "Common.h" #include "StartFormatCommand.h" /** Command syntax definition **/ struct Command StartFormatCommand = { START_VERB, //!< verb { //!< options {VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {FORCE_OPTION_SHORT, FORCE_OPTION, L"", L"",HELP_FORCE_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", RECOVER_OPTION, L"", L"",L"Recovery Option", FALSE, ValueEmpty} #ifdef OS_BUILD ,{ OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired } #endif }, { //!< targets {FORMAT_TARGET, L"", L"", TRUE, ValueEmpty}, {DIMM_TARGET, L"", HELP_TEXT_DIMM_ID, TRUE, ValueOptional} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Start Format Dimms", //!< help StartFormat, //!< run function TRUE }; EFI_STATUS StartFormat( IN struct Command *pCmd ) { EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; CHAR16 *pTargetValue = NULL; UINT16 *pDimmIds = NULL; UINT32 DimmIdsCount = 0; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; BOOLEAN Force = FALSE; BOOLEAN Confirmation = FALSE; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; UINT32 Index = 0; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; COMMAND_STATUS *pCommandStatus = NULL; BOOLEAN Recovery = FALSE; UINT32 DimmHandle = 0; UINT32 DimmIndex = 0; PRINT_CONTEXT *pPrinterCtx = NULL; NVDIMM_ENTRY(); ZeroMem(DimmStr, sizeof(DimmStr)); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } /** Printing will still work via compatibility mode if NULL so no need to check for NULL. **/ pPrinterCtx = pCmd->pPrintCtx; ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } // initialize status structure ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } if (containsOption(pCmd, RECOVER_OPTION)) { Recovery = TRUE; // Populate the list of DIMM_INFO structures with the DIMMs NOT found in NFIT ReturnCode = pNvmDimmConfigProtocol->GetUninitializedDimmCount(pNvmDimmConfigProtocol, &DimmCount); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (DimmCount == 0) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_INFO_NO_NON_FUNCTIONAL_DIMMS); goto Finish; } pDimms = AllocateZeroPool(sizeof(*pDimms) * DimmCount); if (pDimms == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetUninitializedDimms(pNvmDimmConfigProtocol, DimmCount, pDimms); if (EFI_ERROR(ReturnCode)) { goto Finish; } } else { ReturnCode = GetDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } } // check targets if (ContainTarget(pCmd, DIMM_TARGET)) { pTargetValue = GetTargetValue(pCmd, DIMM_TARGET); ReturnCode = GetDimmIdsFromString(pCmd, pTargetValue, pDimms, DimmCount, &pDimmIds, &DimmIdsCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetDimmIdsFromString"); goto Finish; } if (!Recovery) { if (!AllDimmsInListAreManageable(pDimms, DimmCount, pDimmIds, DimmIdsCount)){ ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_UNMANAGEABLE_DIMM); goto Finish; } } } /* If no dimms specified then use all dimms */ if (DimmIdsCount == 0) { FREE_POOL_SAFE(pDimmIds); pDimmIds = AllocateZeroPool(sizeof(*pDimmIds) * DimmCount); if (pDimmIds == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } for (Index =0; Index < DimmCount; Index++) { pDimmIds[Index] = pDimms[Index].DimmID; } DimmIdsCount = DimmCount; } /** Check force option **/ if (containsOption(pCmd, FORCE_OPTION) || containsOption(pCmd, FORCE_OPTION_SHORT)) { Force = TRUE; } if (!Force) { for (Index = 0; Index < DimmIdsCount; Index++) { ReturnCode = GetDimmHandleByPid(pDimmIds[Index], pDimms, DimmCount, &DimmHandle, &DimmIndex); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = GetPreferredDimmIdAsString(DimmHandle, Recovery ? NULL : pDimms[DimmIndex].DimmUid, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, FORMAT_STR L" " FORMAT_STR L"\n", CLI_FORMAT_DIMM_PROMPT_STR, DimmStr); ReturnCode = PromptYesNo(&Confirmation); if (EFI_ERROR(ReturnCode) || !Confirmation) { ReturnCode = EFI_NOT_STARTED; goto Finish; } } } PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, CLI_FORMAT_DIMM_STARTING_FORMAT); ReturnCode = pNvmDimmConfigProtocol->DimmFormat(pNvmDimmConfigProtocol, pDimmIds, DimmIdsCount, Recovery, pCommandStatus); if (!EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_FORMAT_DIMM_REBOOT_REQUIRED_STR); } else { PRINTER_SET_COMMAND_STATUS(pCmd->pPrintCtx, ReturnCode, CLI_INFO_START_FORMAT, L"", pCommandStatus); ReturnCode = MatchCliReturnCode(pCommandStatus->GeneralStatus); } Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pDimmIds); FREE_POOL_SAFE(pDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the Recover Format command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterStartFormatCommand() { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&StartFormatCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/StartFormatCommand.h000066400000000000000000000013231440615110200216240ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _START_FORMAT_COMMAND_ #define _START_FORMAT_COMMAND_ #include #include "CommandParser.h" /** Register the Format command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterStartFormatCommand(); /** Execute the Format command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS StartFormat( IN struct Command *pCmd ); #endif ipmctl-03.00.00.0485/DcpmPkg/cli/StartSessionCommand.c000066400000000000000000000266611440615110200220260ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include "Common.h" #include "StartSessionCommand.h" #include #include #ifdef OS_BUILD #include "os.h" #include #else #include #endif #define ACTION_SETTING_MODE L"Setting to " FORMAT_STR L" mode.\n" #define MANUAL_ACTION_REQUIRED L"Please manually unload and load the driver\n" #define END_OF_PLAYBACK_BUFFER L"Session tag id not set or at the end of the playback buffer. Setting playback pointer to the first tag.\n\n" #define BUFFERED_FREED_MSG L"Starting a new session will free previously recorded content.\n" /** Helper that creates a new pbr recording buffer @retval EFI_SUCCESS on success **/ STATIC EFI_STATUS CreatePbrBuffer(EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol); STATIC EFI_STATUS SetPbrMode(EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol, UINT32 Mode); STATIC EFI_STATUS ExecuteCommand(CHAR16 *pCmdInput); STATIC EFI_STATUS ExecuteCommands(PRINT_CONTEXT *pPrinterCtx, EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol, UINT32 TagId); STATIC EFI_STATUS ResetPbrSession(EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol, UINT32 Id); extern EFI_DRIVER_BINDING_PROTOCOL gNvmDimmDriverDriverBinding; /** Command syntax definition **/ struct Command StartSessionCommand = { START_VERB, //!< verb { //!< options {FORCE_OPTION_SHORT, FORCE_OPTION, L"", L"", HELP_FORCE_DETAILS_TEXT,FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired }, #endif {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", L"", L"",L"", FALSE, ValueOptional} }, { //!< targets {SESSION_TARGET, L"", L"", TRUE, ValueEmpty}, {PBR_MODE_TARGET, L"", L"", TRUE, ValueRequired}, {PBR_MODE_TAG, L"", L"", FALSE, ValueRequired} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Start a playback or record (PBR) session.", //!< help StartSession, TRUE, TRUE //exclude from PBR }; /** Main cmd handler **/ EFI_STATUS StartSession( IN struct Command *pCmd ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol = NULL; CHAR16 *pModeValue = NULL; CHAR16 *pTagValue = NULL; UINT64 TagId64 = 0; PRINT_CONTEXT *pPrinterCtx = NULL; BOOLEAN Force = FALSE; BOOLEAN Confirmation = FALSE; UINT32 PbrMode; pPrinterCtx = pCmd->pPrintCtx; // NvmDimmConfigProtocol required ReturnCode = OpenNvmDimmProtocol(gNvmDimmPbrProtocolGuid, (VOID **)&pNvmDimmPbrProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } //If Windows, check for admin privilege needed to update registry for PBR state CHECK_WIN_ADMIN_PERMISSIONS(); if (containsOption(pCmd, FORCE_OPTION) || containsOption(pCmd, FORCE_OPTION_SHORT)) { Force = TRUE; } pModeValue = GetTargetValue(pCmd, PBR_MODE_TARGET); if (NULL == pModeValue) { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } pTagValue = GetTargetValue(pCmd, PBR_MODE_TAG); //start recording session if (0 == StrICmp(pModeValue, PBR_RECORD_MODE_VAL)) { ReturnCode = pNvmDimmPbrProtocol->PbrGetMode(&PbrMode); if (EFI_ERROR(ReturnCode)) { Print(FORMAT_STR_NL, CLI_ERR_FAILED_TO_GET_PBR_MODE); goto Finish; } //if currently in a session, warn user that starting a new recording session //will clear previous recording data if (!Force && (PBR_RECORD_MODE == PbrMode || PBR_PLAYBACK_MODE == PbrMode)) { PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, BUFFERED_FREED_MSG); ReturnCode = PromptYesNo(&Confirmation); if (EFI_ERROR(ReturnCode)) { PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_PROMPT_INVALID); NVDIMM_DBG("Failed on PromptedInput"); } //user does not want to proceed, exit command. if (!Confirmation) { goto Finish; } } //create a blank pbr buffer ReturnCode = CreatePbrBuffer(pNvmDimmPbrProtocol); if (EFI_ERROR(ReturnCode)) { Print(FORMAT_STR_NL, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } //set to record mode ReturnCode = SetPbrMode(pNvmDimmPbrProtocol, PBR_RECORD_MODE); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_SET_PBR_MODE, pModeValue); goto Finish; } //reset the tagid to 0 (first tag) PbrDcpmmSerializeTagId(0); //print which mode we just configured PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, ACTION_SETTING_MODE, pModeValue); } //start playback session else if (StrICmp(pModeValue, PBR_PLAYBACK_MANUAL_MODE_VAL) == 0 || StrICmp(pModeValue, PBR_PLAYBACK_MODE_VAL) == 0) { //only print if manual mode, otherwise obvious what mode was just executed. if (0 == StrICmp(pModeValue, PBR_PLAYBACK_MANUAL_MODE_VAL)) { PRINTER_PROMPT_MSG(pPrinterCtx, ReturnCode, ACTION_SETTING_MODE, pModeValue); //default starting session tag is 0x0, unless specified by -tag option. ReturnCode = ResetPbrSession(pNvmDimmPbrProtocol, 0); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_SET_SESSION_TAG); goto Finish; } } //set to playback mode ReturnCode = SetPbrMode(pNvmDimmPbrProtocol, PBR_PLAYBACK_MODE); //no session loaded if (EFI_NOT_READY == ReturnCode) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_NO_PBR_SESSION_LOADED); goto Finish; } else if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_SET_PBR_MODE, pModeValue); goto Finish; } //user specified to playback starting from a specific session tag/id if (pTagValue) { if (GetU64FromString(pTagValue, &TagId64)) { ReturnCode = ResetPbrSession(pNvmDimmPbrProtocol, (UINT32)TagId64); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_RESET_SESSION); goto Finish; } //user is specifying a tag to execute PbrDcpmmSerializeTagId((UINT32)TagId64); } else { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_INTERNAL_ERROR); goto Finish; } } else { ReturnCode = ResetPbrSession(pNvmDimmPbrProtocol, 0); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_RESET_SESSION); goto Finish; } //reset the tagid to 0 (first tag) PbrDcpmmSerializeTagId(0); } //if playback mode, automatically execute pbr buffer if (StrICmp(pModeValue, PBR_PLAYBACK_MODE_VAL) == 0) { ReturnCode = ExecuteCommands(pPrinterCtx, pNvmDimmPbrProtocol, (UINT32)TagId64); } } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_UNKNOWN_MODE); goto Finish; } Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); return ReturnCode; } /** Register the Recover Format command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterStartSessionCommand() { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&StartSessionCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Helper that creates a new pbr recording buffer **/ STATIC EFI_STATUS CreatePbrBuffer(EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol) { if (NULL == pNvmDimmPbrProtocol) { return EFI_INVALID_PARAMETER; } return pNvmDimmPbrProtocol->PbrSetSession(NULL, 0); } /** Helper for setting the playback/record mode **/ STATIC EFI_STATUS SetPbrMode(EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol, UINT32 Mode) { if (NULL == pNvmDimmPbrProtocol) { return EFI_INVALID_PARAMETER; } return pNvmDimmPbrProtocol->PbrSetMode(Mode); } /** Helper for setting the current session tag id **/ STATIC EFI_STATUS ResetPbrSession(EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol, UINT32 Id) { if (NULL == pNvmDimmPbrProtocol) { return EFI_INVALID_PARAMETER; } return pNvmDimmPbrProtocol->PbrResetSession(Id); } /** Helper for executing all cmds within a pbr buffer **/ STATIC EFI_STATUS ExecuteCommands(PRINT_CONTEXT *pPrinterCtx, EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol, UINT32 TagId) { EFI_STATUS ReturnCode; UINT32 TagCount = 0; CHAR16 *pName = NULL; CHAR16 *pDescription = NULL; UINT32 Index = 0; UINT32 Signature; #ifdef OS_BUILD EFI_HANDLE FakeBindHandle = (EFI_HANDLE)0x1; #endif ReturnCode = pNvmDimmPbrProtocol->PbrGetTagCount(&TagCount); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_GET_SESSION_TAG_COUNT); goto Finish; } for (Index = TagId; Index < TagCount; ++Index) { ReturnCode = pNvmDimmPbrProtocol->PbrGetTag(Index, &Signature, &pName, &pDescription, NULL, NULL); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_GET_SESSION_TAG); goto Finish; } #ifdef OS_BUILD PbrDcpmmSerializeTagId(Index+1); #endif if (0 == StrICmp(PBR_DRIVER_INIT_TAG_DESCRIPTION, pName)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, MANUAL_ACTION_REQUIRED); goto Finish; } ResetPbrSession(pNvmDimmPbrProtocol, Index); //if running under the OS, the driver binding start routine needs to run before each command //handler. This will simulate how individual cmds are executed from the CLI. #ifdef OS_BUILD ReturnCode = NvmDimmDriverDriverBindingStart(&gNvmDimmDriverDriverBinding, FakeBindHandle, NULL); /*if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_DURING_DRIVER_INIT); goto Finish; }*/ #endif ExecuteCommand(pName); #ifdef OS_BUILD ReturnCode = NvmDimmDriverDriverBindingStop(&gNvmDimmDriverDriverBinding, FakeBindHandle, 0, NULL); /*if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_DURING_DRIVER_UNINIT); goto Finish; }*/ #endif FREE_POOL_SAFE(pName); FREE_POOL_SAFE(pDescription); } Finish: FREE_POOL_SAFE(pName); FREE_POOL_SAFE(pDescription); return ReturnCode; } /** Helper for executing a single cmd handler based on CLI arg string, i.e. "show -dimm" **/ STATIC EFI_STATUS ExecuteCommand(CHAR16 *pCmdInput) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; struct CommandInput Input; struct Command Command; if (NULL == pCmdInput) { NVDIMM_DBG("pCmdInput value is NULL"); return ReturnCode; } FillCommandInput(pCmdInput, &Input); ReturnCode = Parse(&Input, &Command); if (!EFI_ERROR(ReturnCode)) { /* parse success, now run the command */ ReturnCode = ExecuteCmd(&Command); } FreeCommandInput(&Input); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/StartSessionCommand.h000066400000000000000000000013531440615110200220220ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _START_SESSION_COMMAND_ #define _START_SESSION_COMMAND_ #include #include "CommandParser.h" /** Register the PBR command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterStartSessionCommand(); /** Execute the PBR command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS StartSession( IN struct Command *pCmd ); #endif //_START_SESSION_COMMAND_ ipmctl-03.00.00.0485/DcpmPkg/cli/StopSessionCommand.c000066400000000000000000000100421440615110200216400ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include "Common.h" #include "StopSessionCommand.h" #include #include #ifdef OS_BUILD #include "os.h" #endif #define NO_ACTIVE_SESSION_MSG L"No session running.\n" #define STOP_SESSION_MSG L"Stopped PBR session.\n" #define NORMAL_MODE L"Normal" #define BUFFERED_FREED_MSG L"Stopping a session will free all recording content.\n" /** Command syntax definition **/ struct Command StopSessionCommand = { STOP_VERB, //!< verb { //!< options {FORCE_OPTION_SHORT, FORCE_OPTION, L"", L"",HELP_FORCE_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP,HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired }, #endif {L"", PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, {L"", L"", L"", L"",FALSE, ValueOptional} }, { //!< targets {SESSION_TARGET, L"", L"", TRUE, ValueEmpty} }, {{L"", L"", L"", FALSE, ValueOptional}}, //!< properties L"Stop the active playback or record (PBR) session.", //!< help StopSession, TRUE, TRUE }; /** Main cmd handler **/ EFI_STATUS StopSession( IN struct Command *pCmd ) { BOOLEAN Force = FALSE; EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_PBR_PROTOCOL *pNvmDimmPbrProtocol = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; UINT32 CurPbrMode = PBR_NORMAL_MODE; BOOLEAN Confirmation = FALSE; NVDIMM_ENTRY(); pPrinterCtx = pCmd->pPrintCtx; // NvmDimmConfigProtocol required ReturnCode = OpenNvmDimmProtocol(gNvmDimmPbrProtocolGuid, (VOID **)&pNvmDimmPbrProtocol, NULL); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OPENING_CONFIG_PROTOCOL); ReturnCode = EFI_NOT_FOUND; goto Finish; } //If Windows, check for admin privilege needed to update registry for PBR state CHECK_WIN_ADMIN_PERMISSIONS(); if (containsOption(pCmd, FORCE_OPTION) || containsOption(pCmd, FORCE_OPTION_SHORT)) { Force = TRUE; } ReturnCode = pNvmDimmPbrProtocol->PbrGetMode(&CurPbrMode); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_GET_PBR_MODE); goto Finish; } //if already normal, print a message and exit if (PBR_NORMAL_MODE == CurPbrMode) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, NO_ACTIVE_SESSION_MSG); goto Finish; } if (!Force) { PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, BUFFERED_FREED_MSG); ReturnCode = PromptYesNo(&Confirmation); if (EFI_ERROR(ReturnCode)) { PRINTER_PROMPT_MSG(pCmd->pPrintCtx, ReturnCode, CLI_ERR_PROMPT_INVALID); NVDIMM_DBG("Failed on PromptedInput"); } if (!Confirmation) { goto Finish; } } ReturnCode = pNvmDimmPbrProtocol->PbrSetMode(PBR_NORMAL_MODE); if (EFI_ERROR(ReturnCode)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_FAILED_TO_SET_PBR_MODE, NORMAL_MODE); goto Finish; } //reset the tagid to 0 (first tag) PbrDcpmmSerializeTagId(0); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, STOP_SESSION_MSG); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Register the Recover Format command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterStopSessionCommand() { EFI_STATUS ReturnCode; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&StopSessionCommand); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/cli/StopSessionCommand.h000066400000000000000000000013461440615110200216540ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _STOP_SESSION_COMMAND_ #define _STOP_SESSION_COMMAND_ #include #include "CommandParser.h" /** Register the PBR command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterStopSessionCommand(); /** Execute the PBR command @param[in] pCmd command from CLI @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pCmd is NULL or invalid command line parameters @retval EFI_NOT_READY Invalid device state to perform action **/ EFI_STATUS StopSession( IN struct Command *pCmd ); #endif //_STOP_SESSION_COMMAND_ ipmctl-03.00.00.0485/DcpmPkg/common/000077500000000000000000000000001440615110200164305ustar00rootroot00000000000000ipmctl-03.00.00.0485/DcpmPkg/common/Convert.c000066400000000000000000000756351440615110200202340ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include /** Convert GUID structure to string Caller is responsible for FreePool on this pointer @param[in] pGuid @retval Pointer to string (NULL if pGuid parameter is NULL) @retval is pGuid is NULL or memory allocation failed **/ CHAR16* GuidToStr ( IN EFI_GUID *pGuid ) { CHAR16 *pString = NULL; if (pGuid == NULL) { return NULL; } pString = CatSPrint(NULL, L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", pGuid->Data1, pGuid->Data2, pGuid->Data3, pGuid->Data4[0], pGuid->Data4[1], pGuid->Data4[2], pGuid->Data4[3], pGuid->Data4[4], pGuid->Data4[5], pGuid->Data4[6], pGuid->Data4[7]); return pString; } /** Converts 128 bit unsigned integer to an Unicode string as decimal value. The caller is responsible for memory deallocation of returned pointer. @param[in] Value - 128 bit unsigned integer to convert. @retval NULL, there was an error with memory allocation. @retval address of the allocated Unicode string containing the provided uint128 text representation. **/ CHAR16 * Uint128ToNewString( IN UINT128 Value ) { CHAR16 String[UINT128_STRING_SIZE]; CHAR16 *pToReturn = NULL; SetMem(String, sizeof(String), 0x0); Uint128ToString(Value, String); pToReturn = CatSPrintClean(NULL, FORMAT_STR, String); return pToReturn; } /** Converts 128 bit unsigned integer to an Unicode string as decimal value. @param[in] Value - 128 bit unsigned integer to convert. @param[in,out] String - Unicode string containing the provided uint128 text representation **/ VOID Uint128ToString( IN UINT128 Value, IN OUT CHAR16 String[UINT128_STRING_SIZE] ) { CHAR16 Digits[UINT128_DIGITS]; INT32 Index = 0; INT32 Index2 = 0; UINT64 PartValue = 0; UINT8 Counter = 0; SetMem(Digits, sizeof(Digits), 0x0); if (String == NULL) { return; } ZeroMem(String, UINT128_STRING_SIZE); PartValue = Value.Uint64_1; // This loop needs to be executed twice. Once for Value.Uint64_1 and once for Value.Uint64 for (Counter = 0; Counter < 2; Counter++) { for (Index = (UINT64_BITS - 1); Index >= 0; Index--) { if ((PartValue >> Index) & 1) { Digits[0]++; } if (Index > 0 || Counter == 0) { for (Index2 = 0; Index2 < UINT128_DIGITS; Index2++) { Digits[Index2] *= 2; } } for (Index2 = 0; Index2 < (UINT128_DIGITS - 1); Index2++) { Digits[Index2 + 1] += Digits[Index2] / 10; Digits[Index2] %= 10; } } PartValue = Value.Uint64; } for (Index = (UINT128_DIGITS - 1); Index > 0; Index--) { if (Digits[Index] > 0) { break; } } for (Index2 = Index; Index2 > -1; Index2--) { Digits[Index2] += '0'; } for (Index2 = 0; Index > -1; Index--, Index2++) { String[Index2] = Digits[Index]; } /** set the end of string char overtly, although it is not necessary because of ZeroMem **/ String[Index2] = '\0'; } /** Converts an 8 bit ASCII Source buffer to a Null-terminated Unicode Destination string. The ASCII Source buffer may or may not be Null-terminated, and hence the function safely appends it with a Null-terminator character. The caller is responsible to make sure that Destination points to a buffer with size equal to ((sizeof(Source) + 1) * sizeof(CHAR16)) in bytes. @param[in] Source Pointer to the ASCII Source buffer @param[in] Length Size of the Source buffer in bytes @param[out] Destination Pointer to the Null-terminated Unicode Destination string @retval EFI_SUCCESS The conversion was successful. @retval EFI_INVALID_PARAMETER A parameter was NULL or invalid. @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS EFIAPI SafeAsciiStrToUnicodeStr ( IN CONST CHAR8 *Source, IN UINT32 Length, OUT CHAR16 *Destination ) { CHAR8 *TempSource = NULL; if (Source == NULL || Destination == NULL) { return EFI_INVALID_PARAMETER; } TempSource = AllocateZeroPool(Length + 1); if (TempSource == NULL) { return EFI_OUT_OF_RESOURCES; } CopyMem_S(TempSource, Length + 1, Source, Length); TempSource[Length] = '\0'; AsciiStrToUnicodeStrS(TempSource, Destination, Length + 1); FREE_POOL_SAFE(TempSource); return EFI_SUCCESS; } /** Check if a Unicode character is a decimal character. This internal function checks if a Unicode character is a decimal character. The valid characters are L'0' to L'9'. @param[in] Char The character to check against. @retval TRUE If the Char is a hexadecimal character. @retval FALSE If the Char is not a hexadecimal character. **/ BOOLEAN EFIAPI NvmIsDecimalDigitCharacter ( IN CHAR16 Char ) { return (BOOLEAN) (Char >= L'0' && Char <= L'9'); } /** Convert a Unicode character to upper case only if it maps to a valid small-case ASCII character. This internal function only deal with Unicode character which maps to a valid small-case ASCII character, i.e. L'a' to L'z'. For other Unicode character, the input character is returned directly. @param[in] Char The character to convert. @retval LowerCharacter If the Char is with range L'a' to L'z'. @retval Unchanged Otherwise. **/ CHAR16 EFIAPI CrCharToUpper ( IN CHAR16 Char ) { if (Char >= L'a' && Char <= L'z') { return (CHAR16) (Char - (L'a' - L'A')); } return Char; } /** Check if a Unicode character is a hexadecimal character. This internal function checks if a Unicode character is a numeric character. The valid hexadecimal characters are L'0' to L'9', L'a' to L'f', or L'A' to L'F'. @param[in] Char The character to check against. @retval TRUE If the Char is a hexadecimal character. @retval FALSE If the Char is not a hexadecimal character. **/ BOOLEAN EFIAPI NvmIsHexaDecimalDigitCharacter ( IN CHAR16 Char ) { return (BOOLEAN) ((Char >= L'0' && Char <= L'9') || (Char >= L'A' && Char <= L'F') || (Char >= L'a' && Char <= L'f')); } /** Function to determine if an entire string is a valid number. If Hex it must be preceded with a 0x or has ForceHex, set TRUE. @param[in] pString The string to evaluate. @param[in] ForceHex TRUE - always assume hex. @param[in] StopAtSpace TRUE to halt upon finding a space, FALSE to keep going. @retval TRUE It is all numeric (dec/hex) characters. @retval FALSE There is a non-numeric character. **/ BOOLEAN EFIAPI IsHexOrDecimalNumber ( IN CONST CHAR16 *pString, IN CONST BOOLEAN ForceHex, IN CONST BOOLEAN StopAtSpace ) { BOOLEAN Hex; // // chop off a single negative sign // if (pString != NULL && *pString == L'-') { pString++; } if (pString == NULL) { return (FALSE); } // // chop leading zeros // while(pString != NULL && *pString == L'0'){ pString++; } // // allow '0x' or '0X', but not 'x' or 'X' // if (pString != NULL && (*pString == L'x' || *pString == L'X')) { if (*(pString-1) != L'0') { // // we got an x without a preceding 0 // return (FALSE); } pString++; Hex = TRUE; } else if (ForceHex) { Hex = TRUE; } else { Hex = FALSE; } // // loop through the remaining characters and use the lib function // for ( ; pString != NULL && *pString != CHAR_NULL && !(StopAtSpace && *pString == L' ') ; pString++){ if (Hex) { if (!NvmIsHexaDecimalDigitCharacter(*pString)) { return (FALSE); } } else { if (!NvmIsDecimalDigitCharacter(*pString)) { return (FALSE); } } } return (TRUE); } /** Check if a Unicode character is a hexadecimal value. This internal function checks if a Unicode character is a numeric value. @param[in] pString The character to check against. @param[in] StopAtSpace TRUE to halt upon finding a space, FALSE to keep going. @retval TRUE If the pString is a hexadecimal number. @retval FALSE If the pString is not a hexadecimal number. **/ BOOLEAN EFIAPI IsHexValue( IN CONST CHAR16 *pString, IN CONST BOOLEAN StopAtSpace ) { // // chop off a single negative sign // if (pString != NULL && *pString == L'-') { pString++; } if (pString == NULL) { return (FALSE); } // // chop leading zeros // while(pString != NULL && *pString == L'0') { pString++; } // // allow '0x' or '0X', but not 'x' or 'X' // if (pString != NULL && (*pString == L'x' || *pString == L'X')) { if (*(pString-1) != L'0') { // // we got an x without a preceding 0 // return FALSE; } pString++; } else { return FALSE; } // // loop through the remaining characters and use the lib function // for ( ; pString != NULL && *pString != CHAR_NULL && !(StopAtSpace && *pString == L' ') ; pString++) { if (!NvmIsHexaDecimalDigitCharacter(*pString)) { return FALSE; } } return TRUE; } /** Check ASCII character for being a alphanumeric character @param[in] Char Character to be tested @retval TRUE Provided char is an alphanumeric character @retval FALSE Provided char is not an alphanumeric character **/ BOOLEAN EFIAPI IsAsciiAlnumCharacter ( IN CHAR8 Char ) { return (BOOLEAN) ((Char >= '0' && Char <= '9') || (Char >= 'A' && Char <= 'Z') || (Char >= 'a' && Char <= 'z')); } /** Check Unicode character for being a alphanumeric character @param[in] Char Character to be tested @retval TRUE Provided char is an alphanumeric character @retval FALSE Provided char is not an alphanumeric character **/ BOOLEAN EFIAPI IsUnicodeAlnumCharacter ( IN CHAR16 Char ) { return (BOOLEAN) ((Char >= L'0' && Char <= L'9') || (Char >= L'A' && Char <= L'Z') || (Char >= L'a' && Char <= L'z')); } /** Convert a Null-terminated Unicode decimal string to a value of type UINT64. This function returns a value of type UINT64 by interpreting the contents of the Unicode string specified by String as a decimal number. The format of the input Unicode string String is: [spaces] [decimal digits]. The valid decimal digit character is in the range [0-9]. The function will ignore the pad space, which includes spaces or tab characters, before [decimal digits]. The running zero in the beginning of [decimal digits] will be ignored. Then, the function stops at the first character that is a not a valid decimal character or a Null-terminator, whichever one comes first. If String has only pad spaces, then 0 is returned. If String has no pad spaces or valid decimal digits, then 0 is returned. @param[in] pString A pointer to a Null-terminated Unicode string. @param[out] pValue Upon a successful return the value of the conversion. @param[in] StopAtSpace FALSE to skip spaces. @retval EFI_SUCCESS The conversion was successful. @retval EFI_INVALID_PARAMETER A parameter was NULL or invalid. @retval EFI_DEVICE_ERROR An overflow occurred. **/ EFI_STATUS EFIAPI CrStrDecimalToUint64 ( IN CONST CHAR16 *pString, OUT UINT64 *pValue, IN CONST BOOLEAN StopAtSpace ) { UINT64 Result = 0; if (pString == NULL || StrSize (pString) == 0 || pValue == NULL) { return (EFI_INVALID_PARAMETER); } // // Ignore the pad spaces (space or tab) // while ((*pString == L' ') || (*pString == L'\t')) { pString++; } // // Ignore leading Zeros after the spaces // while (*pString == L'0') { pString++; } Result = 0; // // Skip spaces if requested // while (StopAtSpace && *pString == L' ') { pString++; } while (NvmIsDecimalDigitCharacter (*pString)) { // // If the number represented by String overflows according // to the range defined by UINT64, then ASSERT(). // if (!(Result <= (DivU64x32((((UINT64) ~0) - (*pString - L'0')),10)))) { return (EFI_DEVICE_ERROR); } Result = MultU64x32(Result, 10) + (*pString - L'0'); pString++; // // Stop at spaces if requested // if (StopAtSpace && *pString == L' ') { break; } } *pValue = Result; return (EFI_SUCCESS); } /** Convert a Unicode character to numerical value. This internal function only deal with Unicode character which maps to a valid hexadecimal ASCII character, i.e. L'0' to L'9', L'a' to L'f' or L'A' to L'F'. For other Unicode character, the value returned does not make sense. @param[in] Char The character to convert. @retval The numerical value converted. **/ UINT32 EFIAPI CrHexCharToUint ( IN CHAR16 Char ) { if (NvmIsDecimalDigitCharacter (Char)) { return Char - L'0'; } return 10 + CrCharToUpper (Char) - L'A'; } /** Convert a Null-terminated Unicode hexadecimal string to a value of type UINT64. This function returns a value of type UINTN by interpreting the contents of the Unicode string specified by String as a hexadecimal number. The format of the input Unicode string String is: [spaces][zeros][x][hexadecimal digits]. The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F]. The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix. If "x" appears in the input string, it must be prefixed with at least one 0. The function will ignore the pad space, which includes spaces or tab characters, before [zeros], [x] or [hexadecimal digit]. The running zero before [x] or [hexadecimal digit] will be ignored. Then, the decoding starts after [x] or the first valid hexadecimal digit. Then, the function stops at the first character that is a not a valid hexadecimal character or NULL, whichever one comes first. If String has only pad spaces, then zero is returned. If String has no leading pad spaces, leading zeros or valid hexadecimal digits, then zero is returned. @param[in] pString A pointer to a Null-terminated Unicode string. @param[out] pValue Upon a successful return the value of the conversion. @param[in] StopAtSpace FALSE to skip spaces. @retval EFI_SUCCESS The conversion was successful. @retval EFI_INVALID_PARAMETER A parameter was NULL or invalid. @retval EFI_DEVICE_ERROR An overflow occurred. **/ EFI_STATUS EFIAPI CrStrHexToUint64 ( IN CONST CHAR16 *pString, OUT UINT64 *pValue, IN CONST BOOLEAN StopAtSpace ) { UINT64 Result = 0; if (pString == NULL || StrSize(pString) == 0 || pValue == NULL) { return (EFI_INVALID_PARAMETER); } // // Ignore the pad spaces (space or tab) // while ((*pString == L' ') || (*pString == L'\t')) { pString++; } // // Ignore leading Zeros after the spaces // while (*pString == L'0') { pString++; } if (CrCharToUpper (*pString) == L'X') { if (*(pString - 1) != L'0') { return 0; } // // Skip the 'X' // pString++; } Result = 0; // // Skip spaces if requested // while (StopAtSpace && *pString == L' ') { pString++; } while (NvmIsHexaDecimalDigitCharacter (*pString)) { // // If the Hex Number represented by String overflows according // to the range defined by UINTN, then ASSERT(). // if (!(Result <= (RShiftU64((((UINT64) ~0) - CrHexCharToUint (*pString)), 4)))) { // if (!(Result <= ((((UINT64) ~0) - CrHexCharToUintn (*String)) >> 4))) { return (EFI_DEVICE_ERROR); } Result = (LShiftU64(Result, 4)); Result += CrHexCharToUint (*pString); pString++; // // stop at spaces if requested // if (StopAtSpace && *pString == L' ') { break; } } *pValue = Result; return (EFI_SUCCESS); } /** Function to verify and convert a string to its numerical value. If Hex it must be preceded with a 0x, 0X, or has ForceHex set TRUE. @param[in] pString The string to evaluate. @param[out] pValue Upon a successful return the value of the conversion. @param[in] ForceHex TRUE - always assume hex. @param[in] StopAtSpace FALSE to skip spaces. @retval EFI_SUCCESS The conversion was successful. @retval EFI_INVALID_PARAMETER String contained an invalid character. @retval EFI_NOT_FOUND String was a number, but Value was NULL. **/ EFI_STATUS EFIAPI ConvertStringToUint64( IN CONST CHAR16 *pString, OUT UINT64 *pValue, IN CONST BOOLEAN ForceHex, IN CONST BOOLEAN StopAtSpace ) { UINT64 RetVal = 0; CONST CHAR16 *pWalker = NULL; EFI_STATUS Status = EFI_SUCCESS; BOOLEAN Hex = FALSE; Hex = ForceHex; if (!IsHexOrDecimalNumber(pString, Hex, StopAtSpace)) { if (!Hex) { Hex = TRUE; if (!IsHexOrDecimalNumber(pString, Hex, StopAtSpace)) { return (EFI_INVALID_PARAMETER); } } else { return (EFI_INVALID_PARAMETER); } } // // Chop off leading spaces // for (pWalker = pString; pWalker != NULL && *pWalker != CHAR_NULL && *pWalker == L' '; pWalker++); // // make sure we have something left that is numeric. // if (pWalker == NULL || *pWalker == CHAR_NULL || !IsHexOrDecimalNumber(pWalker, Hex, StopAtSpace)) { return (EFI_INVALID_PARAMETER); } // // do the conversion. // if (Hex || StrnCmp(pWalker, L"0x", 2) == 0 || StrnCmp(pWalker, L"0X", 2) == 0){ Status = CrStrHexToUint64(pWalker, &RetVal, StopAtSpace); } else { Status = CrStrDecimalToUint64(pWalker, &RetVal, StopAtSpace); } if (pValue == NULL && !EFI_ERROR(Status)) { return (EFI_NOT_FOUND); } if (pValue != NULL) { *pValue = RetVal; } return (Status); } /** Checks if the provided Unicode string is a proper hex or dec value and decodes the value. The result is stored at the pOutValue pointer. @param[in] pString is the pointer to an Unicode string that the caller wants to check (and parse) if it contains hex or dec value. @param[out] pOutValue is the pointer to a UINT64 value where the decoded result will be stored. @retval TRUE if the provided string is a valid hex or dec value and it was successfully decoded. @retval FALSE if the provided string is not a valid hex or dec value or there was an error while decoding its value. **/ BOOLEAN GetU64FromString ( IN CHAR16 *pString, OUT UINT64 *pOutValue ) { BOOLEAN IsValid = FALSE; EFI_STATUS ReturnCode = EFI_SUCCESS; if (pString == NULL || pOutValue == NULL) { NVDIMM_DBG("pString or pOutValue is NULL."); return FALSE; } // negative values shouldn't be converted if (StrnCmp(pString, L"-", 1) == 0) { return FALSE; } IsValid = IsHexOrDecimalNumber(pString, FALSE, TRUE); if (!IsValid) { return FALSE; } ReturnCode = ConvertStringToUint64(pString, pOutValue, FALSE, TRUE); if (EFI_ERROR(ReturnCode)) { return FALSE; } return TRUE; } /** A helper function to convert a capacity value in bytes as per the requested units to a printable string. Value will be round to NumberOfDigitsAfterDecimal. @param[in] Capacity The input capacity in bytes @param[in] Units The requested type of units to convert the capacity into @param[in] NumberOfDigitsAfterDecimal Number of digits to round to after the decimal point @param[out] pFormattedSizeString Pointer to the converted size string Note: The caller is responsible for freeing the returned string Note: The returned value will always be less than the actual value (don't over-report size) @retval EFI_INVALID_PARAMETER if one or more input parameters are invalid @retval EFI_SUCCESS The conversion was successful **/ EFI_STATUS GetFormattedSizeString ( IN UINT64 Capacity, IN UINT16 Units, IN UINT32 NumberOfDigitsAfterDecimal, OUT CHAR16 **ppFormattedSizeString ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT64 ValueBeforeDecimal = 0; UINT64 RemainderBytes = 0; UINT64 ValueAfterDecimal = 0; // Truncate to an extra decimal place, then round UINT32 NumberOfDigitsAfterDecimalTruncate = NumberOfDigitsAfterDecimal + 1; NVDIMM_ENTRY(); if (ppFormattedSizeString == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Note: This function is using integers for computation instead of floats // to avoid triggering the precision bit in the MXCSR register. /* // Example run-through. Result should be 2.1 KiB Value = 2048 + (1024+6)/10 = 2151 1024 = Bytes in KiB Before = 2151 / 1024 = 2 RemainderBytes = (2151 - (Before * 1024)) = 103 After = (RemainderBytes * (10^Digits))/1024 = 1 */ switch(Units) { case DISPLAY_SIZE_UNIT_B: ValueBeforeDecimal = Capacity; ValueAfterDecimal = 0; break; case DISPLAY_SIZE_UNIT_MIB: ValueBeforeDecimal = BYTES_TO_MIB(Capacity); RemainderBytes = Capacity - MIB_TO_BYTES(ValueBeforeDecimal); ValueAfterDecimal = BYTES_TO_MIB(RemainderBytes * (Pow(10, NumberOfDigitsAfterDecimalTruncate))); break; case DISPLAY_SIZE_UNIT_MB: ValueBeforeDecimal = BYTES_TO_MB(Capacity); RemainderBytes = Capacity - MB_TO_BYTES(ValueBeforeDecimal); ValueAfterDecimal = BYTES_TO_MB(RemainderBytes * (Pow(10, NumberOfDigitsAfterDecimalTruncate))); break; case DISPLAY_SIZE_UNIT_GIB: ValueBeforeDecimal = BYTES_TO_GIB(Capacity); RemainderBytes = Capacity - GIB_TO_BYTES(ValueBeforeDecimal); ValueAfterDecimal = BYTES_TO_GIB(RemainderBytes * (Pow(10, NumberOfDigitsAfterDecimalTruncate))); break; case DISPLAY_SIZE_UNIT_GB: ValueBeforeDecimal = BYTES_TO_GB(Capacity); RemainderBytes = Capacity - GB_TO_BYTES(ValueBeforeDecimal); ValueAfterDecimal = BYTES_TO_GB(RemainderBytes * (Pow(10, NumberOfDigitsAfterDecimalTruncate))); break; case DISPLAY_SIZE_UNIT_TIB: ValueBeforeDecimal = BYTES_TO_TIB(Capacity); RemainderBytes = Capacity - TIB_TO_BYTES(ValueBeforeDecimal); ValueAfterDecimal = BYTES_TO_TIB(RemainderBytes * (Pow(10, NumberOfDigitsAfterDecimalTruncate))); break; case DISPLAY_SIZE_UNIT_TB: ValueBeforeDecimal = BYTES_TO_TB(Capacity); RemainderBytes = Capacity - TB_TO_BYTES(ValueBeforeDecimal); ValueAfterDecimal = BYTES_TO_TB(RemainderBytes * (Pow(10, NumberOfDigitsAfterDecimalTruncate))); break; default: ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Round up/down to the nearest decimal requested ValueAfterDecimal = ROUND_CLOSEST(ValueAfterDecimal, 10); // If rounded up to 10, need to fix it up. if (Pow(10, NumberOfDigitsAfterDecimal) == ValueAfterDecimal) { ValueBeforeDecimal += 1; ValueAfterDecimal = 0; } // If bytes / no decimal digits then no decimal needed if (Units == DISPLAY_SIZE_UNIT_B || NumberOfDigitsAfterDecimal == 0) { *ppFormattedSizeString = CatSPrintClean(*ppFormattedSizeString, FORMAT_UINT64, ValueBeforeDecimal); } else { *ppFormattedSizeString = CatSPrintClean(*ppFormattedSizeString, FORMAT_UINT64 L"." FORMAT_DYNAMIC_WIDTH_LEADING_ZEROS, ValueBeforeDecimal, (UINTN)NumberOfDigitsAfterDecimal, ValueAfterDecimal); } if (*ppFormattedSizeString == NULL) { NVDIMM_DBG("Could not allocate string"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Make the capacity string based on the requested units @param[in] HiiHandle The handle for the hii instance (used for string pack) @param[in] Capacity The input capacity in bytes @param[in] CurrentUnits The requested type of units to convert the capacity into @param[in] AppendUnits Flag to append units to the resulting capacity string @param[out] ppCapacityStr Pointer to string that displays the capacity and units @retval EFI_INVALID_PARAMETER if input parameter is null @retval EFI_SUCCESS The conversion was successful **/ EFI_STATUS MakeCapacityString ( IN EFI_HII_HANDLE HiiHandle, IN UINT64 Capacity, IN UINT16 CurrentUnits, IN BOOLEAN AppendUnits, OUT CHAR16 **ppCapacityStr ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT16 UnitsSelector = 0; CHAR16 *pUnitsStr = NULL; CHAR16 *pUnitsStr2 = NULL; CHAR16 *pFormattedSizeString = NULL; NVDIMM_ENTRY(); UnitsSelector = CurrentUnits; if (ppCapacityStr == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Check if field is unknown (PMTT table is missing). If so, use "Unknown" instead if (Capacity == ACPI_TABLE_VALUE_UNKNOWN) { *ppCapacityStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_UNKNOWN), NULL); goto Finish; } /** Get the best way to display the capacity if the request is AUTO/AUTO_10 **/ if ((CurrentUnits == DISPLAY_SIZE_UNIT_AUTO) || (CurrentUnits == DISPLAY_SIZE_UNIT_AUTO_10)) { UnitsSelector = GetBestDisplayForCapacity(Capacity, CurrentUnits); } CHECK_RESULT(GetFormattedSizeString(Capacity, UnitsSelector, DIGITS_AFTER_DECIMAL_THREE, &pFormattedSizeString), Finish); if (!AppendUnits) { pUnitsStr = CatSPrintClean(NULL, L""); } else { CHECK_RESULT(UnitsToStr(HiiHandle, UnitsSelector, &pUnitsStr2), Finish); pUnitsStr = CatSPrintClean(NULL, L" " FORMAT_STR, pUnitsStr2); } *ppCapacityStr = CatSPrintClean(*ppCapacityStr, FORMAT_STR FORMAT_STR, pFormattedSizeString, pUnitsStr); Finish: FREE_POOL_SAFE(pUnitsStr); FREE_POOL_SAFE(pUnitsStr2); FREE_POOL_SAFE(pFormattedSizeString); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Gets the best possible units to display capacity @param[in] Capacity The input capacity in bytes @param[in] CurrentUnits The units for best possible representation of capacity @retval The optimal units to use for the given combination **/ UINT16 GetBestDisplayForCapacity ( IN UINT64 Capacity, IN UINT16 CurrentUnits) { UINT16 OptimalUnits = DISPLAY_SIZE_UNIT_GIB; if (CurrentUnits == DISPLAY_SIZE_UNIT_AUTO) { if (((Capacity/BYTES_IN_MEBIBYTE)/BYTES_IN_MEBIBYTE > 0) != 0) { OptimalUnits = DISPLAY_SIZE_UNIT_TIB; } else if ((Capacity/BYTES_IN_GIBIBYTE > 0) != 0) { OptimalUnits = DISPLAY_SIZE_UNIT_GIB; } else if ((Capacity/BYTES_IN_MEBIBYTE > 0) != 0) { OptimalUnits = DISPLAY_SIZE_UNIT_MIB; } else { OptimalUnits = DISPLAY_SIZE_UNIT_B; } } else if (CurrentUnits == DISPLAY_SIZE_UNIT_AUTO_10) { if (((Capacity/BYTES_IN_MEGABYTE)/BYTES_IN_MEGABYTE > 0) != 0) { OptimalUnits = DISPLAY_SIZE_UNIT_TB; } else if ((Capacity/BYTES_IN_GIGABYTE > 0) != 0) { OptimalUnits = DISPLAY_SIZE_UNIT_GB; } else if ((Capacity/BYTES_IN_MEGABYTE > 0) != 0) { OptimalUnits = DISPLAY_SIZE_UNIT_MB; } else { OptimalUnits = DISPLAY_SIZE_UNIT_B; } } return OptimalUnits; } /** Get preferred value as string @param[in] Number Value as number @param[in] pString Value as string @param[in] NumberPreferred True if the number is preferred @param[out] pResultString String representation of preferred value @param[in] ResultStringLen Length of pResultString @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS GetPreferredValueAsString( IN UINT32 Number, IN CHAR16 *pString OPTIONAL, IN BOOLEAN NumberPreferred, OUT CHAR16 *pResultString, IN UINT32 ResultStringLen ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pResultString == NULL) { goto Finish; } if (pString == NULL || NumberPreferred) { if (Number > 0xFFFF) { UnicodeSPrint(pResultString, (ResultStringLen - 1) * sizeof(*pResultString), L"0x%08x", Number); } else { UnicodeSPrint(pResultString, (ResultStringLen - 1) * sizeof(*pResultString), L"0x%04x", Number); } } else { StrnCpyS(pResultString, ResultStringLen, pString, ResultStringLen - 1); } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Safely computes and populates capacity in bytes using the conversion factor for the particular units @param[in] Capacity The input capacity @param[in] ConversionFactor Value used to convert to bytes (based on the particular units) @param[out] pCapacityBytes Pointer to capacity in bytes @retval EFI_SUCCESS if the computation was successful @retval EFI_INVALID_PARAMETER if one or more input parameters are invalid @retval EFI_BAD_BUFFER_SIZE if overflow check fails for the input capacity because of buffer size limitations **/ EFI_STATUS SafeCapacityConversionToBytes ( IN double Capacity, IN UINT64 ConversionFactor, OUT UINT64 *pCapacityBytes ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if ((ConversionFactor == 0) || (pCapacityBytes == NULL)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } //Overflow check if (Capacity > (double)(MAX_UINT64_VALUE / ConversionFactor)) { ReturnCode = EFI_BAD_BUFFER_SIZE; goto Finish; } *pCapacityBytes = (UINT64)(Capacity * ConversionFactor); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Convert the given capacity into bytes @param[in] Capacity The input capacity @param[in] Units The units representing the given capacity @param[out] pCapacityBytes Pointer to return value - Capacity in bytes @retval EFI_SUCCESS if the conversion was successful @retval EFI_INVALID_PARAMETER if the input parameter was incorrect or null **/ EFI_STATUS ConvertToBytes ( IN double Capacity, IN UINT16 Units, OUT UINT64 *pCapacityBytes) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pCapacityBytes == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } switch(Units) { case DISPLAY_SIZE_UNIT_B: *pCapacityBytes = (UINT64) Capacity; break; case DISPLAY_SIZE_UNIT_MIB: ReturnCode = SafeCapacityConversionToBytes(Capacity, BYTES_IN_MEBIBYTE, pCapacityBytes); break; case DISPLAY_SIZE_UNIT_MB: ReturnCode = SafeCapacityConversionToBytes(Capacity, BYTES_IN_MEGABYTE, pCapacityBytes); break; case DISPLAY_SIZE_UNIT_GIB: ReturnCode = SafeCapacityConversionToBytes(Capacity, BYTES_IN_GIBIBYTE, pCapacityBytes); break; case DISPLAY_SIZE_UNIT_GB: ReturnCode = SafeCapacityConversionToBytes(Capacity, BYTES_IN_GIGABYTE, pCapacityBytes); break; case DISPLAY_SIZE_UNIT_TIB: ReturnCode = SafeCapacityConversionToBytes(Capacity, BYTES_IN_TEBIBYTE, pCapacityBytes); break; case DISPLAY_SIZE_UNIT_TB: ReturnCode = SafeCapacityConversionToBytes(Capacity, BYTES_IN_TERABYTE, pCapacityBytes); break; default: ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get physical block size - aligned to cache line size @param[in] BlockSize Usable block size @retval Physical block size **/ UINT64 GetPhysicalBlockSize( IN UINT64 BlockSize ) { return (BlockSize == AD_NAMESPACE_BLOCK_SIZE) ? AD_NAMESPACE_BLOCK_SIZE : ROUNDUP(BlockSize, CACHE_LINE_SIZE); } /** Get granularity size for a stored granularity value @param[in] AppDirectGranularity stored as driver preference value @retval granularity size **/ UINT64 ConvertAppDirectGranularityPreference( IN UINT8 AppDirectGranularity ) { switch (AppDirectGranularity) { case APPDIRECT_GRANULARITY_1GIB: return SIZE_1GB; break; case APPDIRECT_GRANULARITY_32GIB: default: return SIZE_32GB; } } ipmctl-03.00.00.0485/DcpmPkg/common/Convert.h000066400000000000000000000347711440615110200202350ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _CONVERT_H_ #define _CONVERT_H_ #include #include #include #include #include //!< Macro that rounds up x value to the Next value with y multiplier #define ROUNDUP(N, S) ((((N) + (S) - 1) / (S)) * (S)) //!< Macro that rounds down x value to the Next value with y multiplier #define ROUNDDOWN(N, S) ((N) - ((N)%(S))) //!< Macro that rounds up or down to the closest divisor //!< Negative divisors are not supported #define ROUND_CLOSEST(N, D) ((((N) < 0) ^ ((D) < 0)) ? (((N) - (D)/2)/(D)) : (((N) + (D)/2)/(D))) #define DIGITS_AFTER_DECIMAL_ONE 1 #define DIGITS_AFTER_DECIMAL_THREE 3 /** Convert GUID structure to string Caller is responsible for FreePool on this pointer @param[in] pGuid @retval Pointer to string (NULL if pGuid parameter is NULL) @retval is pGuid is NULL or memory allocation failed **/ CHAR16* GuidToStr ( IN GUID *pGuid ); /** Converts 128 bit unsigned integer to an Unicode string as decimal value. The caller is responsible for memory deallocation of returned pointer. @param[in] Value - 128 bit unsigned integer to convert. @retval NULL, there was an error with memory allocation. @retval address of the allocated Unicode string containing the provided uint128 text representation. **/ CHAR16 * Uint128ToNewString( IN UINT128 Value ); /** Converts 128 bit unsigned integer to an Unicode string as decimal value. @param[in] Value - 128 bit unsigned integer to convert. @param[in,out] String - Unicode string containing the provided uint128 text representation **/ VOID Uint128ToString( IN UINT128 Value, IN OUT CHAR16 String[UINT128_STRING_SIZE] ); /** Converts an 8 bit ASCII Source buffer to a Null-terminated Unicode Destination string. The ASCII Source buffer may or may not be Null-terminated, and hence the function safely appends it with a Null-terminator character. The caller is responsible to make sure that Destination points to a buffer with size equal to ((sizeof(Source) + 1) * sizeof(CHAR16)) in bytes. @param[in] Source Pointer to the ASCII Source buffer @param[in] Length Size of the Source buffer in bytes @param[out] Destination Pointer to the Null-terminated Unicode Destination string @retval EFI_SUCCESS The conversion was successful. @retval EFI_INVALID_PARAMETER A parameter was NULL or invalid. @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS EFIAPI SafeAsciiStrToUnicodeStr ( IN CONST CHAR8 *Source, IN UINT32 Length, OUT CHAR16 *Destination ); /** Check if a Unicode character is a decimal character. This internal function checks if a Unicode character is a decimal character. The valid characters are L'0' to L'9'. @param[in] Char The character to check against. @retval TRUE If the Char is a hexadecimal character. @retval FALSE If the Char is not a hexadecimal character. **/ BOOLEAN EFIAPI NvmIsDecimalDigitCharacter ( IN CHAR16 Char ); /** Convert a Unicode character to upper case only if it maps to a valid small-case ASCII character. This internal function only deal with Unicode character which maps to a valid small-case ASCII character, i.e. L'a' to L'z'. For other Unicode character, the input character is returned directly. @param[in] Char The character to convert. @retval LowerCharacter If the Char is with range L'a' to L'z'. @retval Unchanged Otherwise. **/ CHAR16 EFIAPI CrCharToUpper ( IN CHAR16 Char ); /** Check if a Unicode character is a hexadecimal character. This internal function checks if a Unicode character is a numeric character. The valid hexadecimal characters are L'0' to L'9', L'a' to L'f', or L'A' to L'F'. @param[in] Char The character to check against. @retval TRUE If the Char is a hexadecimal character. @retval FALSE If the Char is not a hexadecimal character. **/ BOOLEAN EFIAPI NvmIsHexaDecimalDigitCharacter ( IN CHAR16 Char ); /** Function to determin if an entire string is a valid number. If Hex it must be preceded with a 0x or has ForceHex, set TRUE. @param[in] pString The string to evaluate. @param[in] ForceHex TRUE - always assume hex. @param[in] StopAtSpace TRUE to halt upon finding a space, FALSE to keep going. @retval TRUE It is all numeric (dec/hex) characters. @retval FALSE There is a non-numeric character. **/ BOOLEAN EFIAPI IsHexOrDecimalNumber ( IN CONST CHAR16 *pString, IN CONST BOOLEAN ForceHex, IN CONST BOOLEAN StopAtSpace ); /** Check ASCII character for being a alphanumeric character @param[in] Char Character to be tested @retval TRUE Provided char is an alphanumeric character @retval FALSE Provided char is not an alphanumeric character **/ BOOLEAN EFIAPI IsAsciiAlnumCharacter ( IN CHAR8 Char ); /** Check Unicode character for being a alphanumeric character @param[in] Char Character to be tested @retval TRUE Provided char is an alphanumeric character @retval FALSE Provided char is not an alphanumeric character **/ BOOLEAN EFIAPI IsUnicodeAlnumCharacter ( IN CHAR16 Char ); /** Convert a Null-terminated Unicode decimal string to a value of type UINT64. This function returns a value of type UINT64 by interpreting the contents of the Unicode string specified by String as a decimal number. The format of the input Unicode string String is: [spaces] [decimal digits]. The valid decimal digit character is in the range [0-9]. The function will ignore the pad space, which includes spaces or tab characters, before [decimal digits]. The running zero in the beginning of [decimal digits] will be ignored. Then, the function stops at the first character that is a not a valid decimal character or a Null-terminator, whichever one comes first. If String has only pad spaces, then 0 is returned. If String has no pad spaces or valid decimal digits, then 0 is returned. @param[in] pString A pointer to a Null-terminated Unicode string. @param[out] pValue Upon a successful return the value of the conversion. @param[in] StopAtSpace FALSE to skip spaces. @retval EFI_SUCCESS The conversion was successful. @retval EFI_INVALID_PARAMETER A parameter was NULL or invalid. @retval EFI_DEVICE_ERROR An overflow occurred. **/ EFI_STATUS EFIAPI CrStrDecimalToUint64 ( IN CONST CHAR16 *pString, OUT UINT64 *pValue, IN CONST BOOLEAN StopAtSpace ); /** Convert a Unicode character to numerical value. This internal function only deal with Unicode character which maps to a valid hexadecimal ASCII character, i.e. L'0' to L'9', L'a' to L'f' or L'A' to L'F'. For other Unicode character, the value returned does not make sense. @param[in] Char The character to convert. @retval The numerical value converted. **/ UINT32 EFIAPI CrHexCharToUint ( IN CHAR16 Char ); /** Convert a Null-terminated Unicode hexadecimal string to a value of type UINT64. This function returns a value of type UINTN by interpreting the contents of the Unicode string specified by String as a hexadecimal number. The format of the input Unicode string String is: [spaces][zeros][x][hexadecimal digits]. The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F]. The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix. If "x" appears in the input string, it must be prefixed with at least one 0. The function will ignore the pad space, which includes spaces or tab characters, before [zeros], [x] or [hexadecimal digit]. The running zero before [x] or [hexadecimal digit] will be ignored. Then, the decoding starts after [x] or the first valid hexadecimal digit. Then, the function stops at the first character that is a not a valid hexadecimal character or NULL, whichever one comes first. If String has only pad spaces, then zero is returned. If String has no leading pad spaces, leading zeros or valid hexadecimal digits, then zero is returned. @param[in] pString A pointer to a Null-terminated Unicode string. @param[out] pValue Upon a successful return the value of the conversion. @param[in] StopAtSpace FALSE to skip spaces. @retval EFI_SUCCESS The conversion was successful. @retval EFI_INVALID_PARAMETER A parameter was NULL or invalid. @retval EFI_DEVICE_ERROR An overflow occurred. **/ EFI_STATUS EFIAPI CrStrHexToUint64 ( IN CONST CHAR16 *pString, OUT UINT64 *pValue, IN CONST BOOLEAN StopAtSpace ); /** Function to verify and convert a string to its numerical value. If Hex it must be preceded with a 0x, 0X, or has ForceHex set TRUE. @param[in] pString The string to evaluate. @param[out] pValue Upon a successful return the value of the conversion. @param[in] ForceHex TRUE - always assume hex. @param[in] StopAtSpace FALSE to skip spaces. @retval EFI_SUCCESS The conversion was successful. @retval EFI_INVALID_PARAMETER String contained an invalid character. @retval EFI_NOT_FOUND String was a number, but Value was NULL. **/ EFI_STATUS EFIAPI ConvertStringToUint64( IN CONST CHAR16 *pString, OUT UINT64 *pValue, IN CONST BOOLEAN ForceHex, IN CONST BOOLEAN StopAtSpace ); /** Checks if the provided Unicode string is a proper hex or dec value and decodes the value. The result is stored at the pOutValue pointer. @param[in] pString is the pointer to an Unicode string that the caller wants to check (and parse) if it contains hex or dec value. @param[out] pOutValue is the pointer to a UINT64 value where the decoded result will be stored. @retval TRUE if the provided string is a valid hex or dec value and it was successfully decoded. @retval FALSE if the provided string is not a valid hex or dec value or there was an error while decoding its value. **/ BOOLEAN GetU64FromString ( IN CHAR16 *pString, OUT UINT64 *pOutValue ); /** Make the capacity string based on the requested units @param[in] HiiHandle The handle for the hii instance (used for string pack) @param[in] Capacity The input capacity in bytes @param[in] CurrentUnits The requested type of units to convert the capacity into @param[in] AppendUnits Flag to append units to the resulting capacity string @param[out] ppCapacityStr Pointer to string that displays the capacity and units @retval EFI_INVALID_PARAMETER if input parameter is null @retval EFI_SUCCESS The conversion was successful **/ EFI_STATUS MakeCapacityString ( IN EFI_HII_HANDLE HiiHandle, IN UINT64 Capacity, IN UINT16 CurrentUnits, IN BOOLEAN AppendUnits, OUT CHAR16 **ppCapacityStr ); /** A helper function to convert a capacity value in bytes as per the requested units to a printable string. @param[in] Capacity The input capacity in bytes @param[in] Units The requested type of units to convert the capacity into @param[out] pFormattedSizeString Pointer to the converted size string Note: The caller is responsible for freeing the returned string Note: The returned value will always be less than the actual value (don't over-report size) @retval EFI_INVALID_PARAMETER if one or more input parameters are invalid @retval EFI_SUCCESS The conversion was successful **/ EFI_STATUS GetFormattedSizeString ( IN UINT64 Capacity, IN UINT16 Units, IN UINT32 NumberOfDigitsAfterDecimal, OUT CHAR16 **ppFormattedSizeString ); /** Gets the best possible units to display capacity @param[in] Capacity The input capacity in bytes @param[in] CurrentUnits The units for best possible representation of capacity @retval The optimal units to use for the given combination **/ UINT16 GetBestDisplayForCapacity ( IN UINT64 Capacity, IN UINT16 CurrentUnits ); /** Get preferred value as string @param[in] Number Value as number @param[in] pString Value as string @param[in] NumberPreferred True if the number is preferred @param[out] pResultString String representation of preferred value @param[in] ResultStringLen Length of pResultString @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS GetPreferredValueAsString( IN UINT32 Number, IN CHAR16 *pString OPTIONAL, IN BOOLEAN NumberPreferred, OUT CHAR16 *pResultString, IN UINT32 ResultStringLen ); /** Safely computes and populates capacity in bytes using the conversion factor for the particular units @param[in] Capacity The input capacity @param[in] ConversionFactor Value used to convert to bytes (based on the particular units) @param[out] pCapacityBytes Pointer to capacity in bytes @retval EFI_SUCCESS if the computation was successful @retval EFI_INVALID_PARAMETER if one or more input parameters are invalid @retval EFI_BAD_BUFFER_SIZE if overflow check fails for the input capacity because of buffer size limitations **/ EFI_STATUS SafeCapacityConversionToBytes ( IN double Capacity, IN UINT64 ConversionFactor, OUT UINT64 *pCapacityBytes ); /** Convert the given capacity into bytes @param[in] Capacity The input capacity @param[in] Units The units representing the given capacity @param[out] pCapacityBytes Pointer to return value - Capacity in bytes @retval EFI_SUCCESS if the conversion was successful @retval EFI_INVALID_PARAMETER if the input parameter was incorrect or null **/ EFI_STATUS ConvertToBytes ( IN double Capacity, IN UINT16 Units, OUT UINT64 *pCapacityBytes ); /** Get physical block size - aligned to cache line size @param[in] BlockSize Usable block size @retval Physical block size **/ UINT64 GetPhysicalBlockSize( IN UINT64 BlockSize ); /** Get granularity size for a stored granularity value @param[in] AppDirectGranularity stored as driver preference value @retval granularity size **/ UINT64 ConvertAppDirectGranularityPreference( IN UINT8 AppDirectGranularity ); /** Check if a Unicode character is a hexadecimal value. This internal function checks if a Unicode character is a numeric value. @param[in] pString The character to check against. @param[in] StopAtSpace TRUE to halt upon finding a space, FALSE to keep going. @retval TRUE If the pString is a hexadecimal number. @retval FALSE If the pString is not a hexadecimal number. **/ BOOLEAN EFIAPI IsHexValue( IN CONST CHAR16 *pString, IN CONST BOOLEAN StopAtSpace ); #endif /** _CONVERT_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/DataSet.c000066400000000000000000000647041440615110200201340ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "DataSet.h" #include #define BOOL_TRUE_STR L"True" #define BOOL_FALSE_STR L"False" typedef struct _KEY_VAL { LIST_ENTRY Link; KEY_VAL_INFO KeyValInfo; VOID *Value; CHAR16 *ValueToString; }KEY_VAL; typedef struct _DATA_SET { LIST_ENTRY Link; LIST_ENTRY KeyValueList; LIST_ENTRY DataSetList; VOID *DataSetParent; CHAR16 *Name; BOOLEAN Dirty; VOID *UserData; }DATA_SET; typedef struct _DS_NAME_INFO { CHAR16 *Name; UINT32 InstanceNum; }DS_NAME_INFO; #define DATA_SET_LIST_FOR_EACH_SAFE(Entry, NextEntry, ListHead) \ for(Entry = (ListHead)->ForwardLink, NextEntry = Entry->ForwardLink; \ Entry != (ListHead); \ Entry = NextEntry, NextEntry = Entry->ForwardLink \ ) #define SET_KEY_VALUE(DataSetCtx, RetVal, Key, Val, ValType, ValTypeEnum, Base) \ do { \ KEY_VAL * KeyVal; \ if(!DataSetCtx || !Key) { \ *RetVal = EFI_INVALID_PARAMETER; \ break; \ } \ if(NULL == (KeyVal = SetKeyValue(DataSetCtx, Key, Val, sizeof(ValType)))) { \ *RetVal = EFI_OUT_OF_RESOURCES; \ break; \ } \ KeyVal->ValueToString = CatSPrint(NULL, FormatString(ValTypeEnum, Base), *((ValType*)Val)); \ KeyVal->KeyValInfo.Type = ValTypeEnum; \ *RetVal = EFI_SUCCESS; \ }while(0) VOID FreeAllKeyValuePairs(DATA_SET *DataSet); /* * Set all data sets in the ancestry path to dirty. */ VOID SetAncestorsDirty(DATA_SET_CONTEXT *DataSetCtx) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; while (DataSet) { DataSet->Dirty = TRUE; DataSet = DataSet->DataSetParent; } } /* * Helper to locate a child node with a particular name */ DATA_SET * FindChildDataSetByIndex(DATA_SET *Parent, CHAR16 *Name, UINT32 Index) { LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; DATA_SET *DataSet; DATA_SET_LIST_FOR_EACH_SAFE(Entry, NextEntry, &Parent->DataSetList) { DataSet = BASE_CR(Entry, DATA_SET, Link); if(0 == StrCmp(Name, DataSet->Name)) { if (0 == Index) { return DataSet; } --Index; } } return NULL; } /* * Free a DataSet Struct */ VOID FreeDataSetMem(DATA_SET *DataSet) { if (NULL == DataSet) { return; } FreeAllKeyValuePairs(DataSet); if(DataSet->Name) { FreePool(DataSet->Name); } FreePool(DataSet); } /* * Recursively free all data sets */ VOID FreeAllDataSets(DATA_SET *DataSet) { LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; DATA_SET *ChildDataSet; if (NULL == DataSet) { return; } DATA_SET_LIST_FOR_EACH_SAFE(Entry, NextEntry, &DataSet->DataSetList) { ChildDataSet = BASE_CR(Entry, DATA_SET, Link); FreeAllDataSets(ChildDataSet); } if(!IsListEmpty(&DataSet->Link)) { RemoveEntryList(&DataSet->Link); } FreeDataSetMem(DataSet); } /* * Create a new data set structure */ DATA_SET_CONTEXT* CreateDataSet(DATA_SET_CONTEXT *DataSetCtx, CHAR16 *Name, VOID *UserData) { DATA_SET *NewDataSet = NULL; DATA_SET *ParentCtx = (DATA_SET *)DataSetCtx; if (NULL == Name) { return NULL; } //check that parent doesn't already have child with the same name /*if (ParentCtx && FindChildDataSet(ParentCtx, Name)) { return NULL; }*/ if (NULL == (NewDataSet = (DATA_SET*)AllocateZeroPool(sizeof(DATA_SET)))) { return NULL; } if (NULL == (NewDataSet->Name = CatSPrint(NULL, Name))) { FreePool(NewDataSet); return NULL; } NewDataSet->UserData = UserData; InitializeListHead(&NewDataSet->KeyValueList); InitializeListHead(&NewDataSet->DataSetList); NewDataSet->Dirty = FALSE; if (DataSetCtx) { InsertTailList(&ParentCtx->DataSetList, &NewDataSet->Link); NewDataSet->DataSetParent = (VOID*)ParentCtx; } else { InitializeListHead(&NewDataSet->Link); } return NewDataSet; } /* * Helper for GetDataSet */ VOID GetDataSetNameInfo(CHAR16 *Name, DS_NAME_INFO *NameInfo) { CHAR16 **Toks = NULL; CHAR16 **ToksSecondHalf; UINT32 NumToks = 0; UINT32 NumToksSecondHalf = 0; if (NULL == (Toks = StrSplit(Name, L'[', &NumToks))) { return; } if (1 == NumToks) { NameInfo->Name = CatSPrint(NULL, Name); NameInfo->InstanceNum = 0; } else { NameInfo->Name = CatSPrint(NULL, Toks[0]); if (NULL != (ToksSecondHalf = StrSplit(Toks[1], L']', &NumToksSecondHalf))) { NameInfo->InstanceNum = (UINT32)StrDecimalToUint64(ToksSecondHalf[0]); FreeStringArray(ToksSecondHalf, NumToksSecondHalf); } } FreeStringArray(Toks, NumToks); } /* * Helper for GetDataSet. Frees memory allocated by GetDataSetNameInfo. */ void FreeDataSetNameInfo(DS_NAME_INFO *NameInfo) { if (NULL != NameInfo->Name) { FreePool(NameInfo->Name); } } /* * Retrieve a data set by specifying a path in the form of /sensorlist/dimm[0]/sensor[1] */ DATA_SET_CONTEXT * EFIAPI GetDataSet(DATA_SET_CONTEXT *Root, CHAR16 *NamePath, ...) { CHAR16 **DataSetToks = NULL; UINT32 NumDataSetToks = 0; UINT32 Index = 0; DATA_SET *TempDataSet = (DATA_SET*)Root; DATA_SET *TempCreateNewDataSet = NULL; DS_NAME_INFO NameInfo; CHAR16 *FormattedNamePath; VA_LIST Args; UINT32 CreateIndex = 0; ++NamePath; VA_START(Args, NamePath); FormattedNamePath = CatVSPrint(NULL, NamePath, Args); VA_END(Args); if (NULL == FormattedNamePath) { return NULL; } //split path, result toks are data set names if (NULL == (DataSetToks = StrSplit(FormattedNamePath, L'/', &NumDataSetToks))) { FreePool(FormattedNamePath); return NULL; } FreePool(FormattedNamePath); //Root data set must match first tok //All other toks that don't exist will be created GetDataSetNameInfo(DataSetToks[0], &NameInfo); if (StrCmp(NameInfo.Name, GetDataSetName(Root))) { TempDataSet = NULL; FreeDataSetNameInfo(&NameInfo); goto Finish; } FreeDataSetNameInfo(&NameInfo); //iterate through all data set names under the root //path: /sensorlist/dimm/sensor //iterated toks: dimm, sensor //create data sets that don't exist for (Index = 1; Index < NumDataSetToks; ++Index) { //get info about current data set GetDataSetNameInfo(DataSetToks[Index], &NameInfo); for (CreateIndex = 0; CreateIndex <= NameInfo.InstanceNum; ++CreateIndex) { if (NULL == (TempCreateNewDataSet = FindChildDataSetByIndex(TempDataSet, NameInfo.Name, NameInfo.InstanceNum))) { //create a new data set and add it to the end if (NULL == (TempCreateNewDataSet = CreateDataSet(TempDataSet, NameInfo.Name, NULL))) { TempDataSet = NULL; FreeDataSetNameInfo(&NameInfo); goto Finish; } } } FreeDataSetNameInfo(&NameInfo); TempDataSet = TempCreateNewDataSet; } Finish: FreeStringArray(DataSetToks, NumDataSetToks); return TempDataSet; } /* * Get the next child dataset in the data set. */ DATA_SET_CONTEXT *GetNextChildDataSet(DATA_SET_CONTEXT *DataSetCtx, DATA_SET_CONTEXT *CurrentChildDataSetCtx) { LIST_ENTRY *Entry; DATA_SET *DataSet = (DATA_SET *)DataSetCtx; DATA_SET *ChildDataSet = (DATA_SET *)CurrentChildDataSetCtx; DATA_SET *NextChildDataSet; if (NULL == CurrentChildDataSetCtx) { //GetFirstNode returns original list is empty if (&DataSet->DataSetList == (Entry = GetFirstNode(&DataSet->DataSetList))) { return NULL; } NextChildDataSet = BASE_CR(Entry, DATA_SET, Link); return (DATA_SET_CONTEXT*)NextChildDataSet; } if (NULL != (Entry = GetNextNode(&DataSet->DataSetList, &ChildDataSet->Link))) { //GetNextNode returns original list when Link is the last node in list. if (Entry != &DataSet->DataSetList) { NextChildDataSet = BASE_CR(Entry, DATA_SET, Link); return (DATA_SET_CONTEXT*)NextChildDataSet; } } return NULL; } /* * Does the data set contain children data sets? */ BOOLEAN IsLeaf(DATA_SET_CONTEXT *DataSetCtx) { LIST_ENTRY *Entry; DATA_SET *DataSet = (DATA_SET *)DataSetCtx; if (NULL == DataSetCtx) { return TRUE; } if (&DataSet->DataSetList == (Entry = GetFirstNode(&DataSet->DataSetList))) { return TRUE; } return FALSE; } /* * Helper to walk a data set hierarchy */ VOID RecurseDataSetInternal(DATA_SET_CONTEXT *DataSetCtx, CHAR16 *CurPath, DataSetCallBack CallBackRoutine, DataSetAllChildrenDoneCallBack ChildrenDoneCallBackRoutine, VOID *UserData, VOID *CallbackData, BOOLEAN Sparse) { LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; DATA_SET *ChildDataSet; DATA_SET *DataSet = (DATA_SET*)DataSetCtx; CHAR16 *NewPath = NULL; VOID * TempCallBackRetVal; if (NULL == DataSet || (Sparse && !DataSet->Dirty)) { return; } NewPath = CatSPrint(CurPath, L"/" FORMAT_STR, GetDataSetName(DataSetCtx)); TempCallBackRetVal = CallBackRoutine(DataSetCtx, NewPath, UserData, CallbackData); DATA_SET_LIST_FOR_EACH_SAFE(Entry, NextEntry, &DataSet->DataSetList) { ChildDataSet = BASE_CR(Entry, DATA_SET, Link); RecurseDataSetInternal(ChildDataSet, NewPath, CallBackRoutine, ChildrenDoneCallBackRoutine, UserData, TempCallBackRetVal, Sparse); } if (ChildrenDoneCallBackRoutine) { ChildrenDoneCallBackRoutine(DataSetCtx, NewPath, UserData); } if (NewPath) { FreePool(NewPath); } if (TempCallBackRetVal) { FreePool(TempCallBackRetVal); } } /* * Recurses through data set tree, invoking the callback at each node (data set) in the tree */ VOID RecurseDataSet(DATA_SET_CONTEXT *DataSetCtx, DataSetCallBack CallBackRoutine, DataSetAllChildrenDoneCallBack ChildrenDoneCallBackRoutine, VOID *UserData, BOOLEAN Sparse) { RecurseDataSetInternal(DataSetCtx, NULL, CallBackRoutine, ChildrenDoneCallBackRoutine, UserData, NULL, Sparse); } /* * Free a data set structure */ VOID FreeDataSet(DATA_SET_CONTEXT *DataSetCtx) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; FreeAllDataSets(DataSet); } /* * Append a child data set to a root data set */ VOID AddChildDataSet(DATA_SET_CONTEXT *Root, DATA_SET_CONTEXT *Child) { DATA_SET *RootDataSet = (DATA_SET*)Root; DATA_SET *ChildDataSet = (DATA_SET*)Child; if (NULL != Root && NULL != Child) { InsertTailList(&RootDataSet->DataSetList, &ChildDataSet->Link); } } /* * Set the name of a data set */ VOID SetDataSetName(DATA_SET_CONTEXT *DataSetCtx, CHAR16 *Name) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; if (DataSet) { if (DataSet->Name) { FreePool(DataSet->Name); } DataSet->Name = CatSPrint(NULL, Name); } } /* * Get the name of a data set */ CHAR16 * GetDataSetName(DATA_SET_CONTEXT *DataSetCtx) { DATA_SET *DataSet = (DATA_SET *)DataSetCtx; return DataSet->Name; } /* * set the user's data to the corresponding data set (UserData must be allocated by AllocatePool) */ VOID SetDataSetUserData(DATA_SET_CONTEXT *DataSetCtx, VOID *UserData) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; if(DataSet->UserData) { FreePool(DataSet->UserData); } DataSet->UserData = UserData; } /* * Return the user's data from the corresponding data set */ VOID * GetDataSetUserData(DATA_SET_CONTEXT *DataSetCtx) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; return DataSet->UserData; } /* * Callback routine for squash operation */ VOID * SquashDataSetCb(DATA_SET_CONTEXT *DataSetCtx, CHAR16 *CurPath, VOID *UserData, VOID *ParentUserData) { DATA_SET_CONTEXT *RootDataSet = (DATA_SET_CONTEXT*)UserData; DATA_SET_CONTEXT *NewDataSet = NULL; KEY_VAL_INFO *KvInfo = NULL; CHAR16 *Val = NULL; if (NULL == UserData) { return NULL; } if (IsLeaf(DataSetCtx)) { NewDataSet = CreateDataSet(RootDataSet, GetDataSetName(DataSetCtx), NULL); while (NULL != (KvInfo = GetNextKey(DataSetCtx, KvInfo))) { GetKeyValueWideStr(DataSetCtx, KvInfo->Key, &Val, NULL); if (Val) { SetKeyValueWideStr(NewDataSet, KvInfo->Key, Val); } } KvInfo = NULL; while (NULL != (KvInfo = GetNextKey(RootDataSet, KvInfo))) { GetKeyValueWideStr(RootDataSet, KvInfo->Key, &Val, NULL); if (Val) { SetKeyValueWideStr(NewDataSet, KvInfo->Key, Val); } } } else { while (NULL != (KvInfo = GetNextKey(DataSetCtx, KvInfo))) { GetKeyValueWideStr(DataSetCtx, KvInfo->Key, &Val, NULL); if (Val) { SetKeyValueWideStr(RootDataSet, KvInfo->Key, Val); } } } return NULL; } /* * Squash a data set hierarchy * Before Squash: * -SensorList * --Dimm * --Dimm.DimmId = 0x0001 (key/val pair under Dimm) * ---Sensor * ---Sensor.Value = 32C * After Squash: * -SensorList * --Sensor * --Sensor.DimmId = 0x0001 * --Sensor.Value = 32C */ DATA_SET_CONTEXT *SquashDataSet(DATA_SET_CONTEXT *DataSetCtx) { DATA_SET_CONTEXT *ChildDataSet = NULL; DATA_SET_CONTEXT *NewRootDataSet = NULL; if (IsLeaf(DataSetCtx)) { return DataSetCtx; } if (NULL == (NewRootDataSet = CreateDataSet(NULL, GetDataSetName(DataSetCtx), NULL))) { return NewRootDataSet; } while (NULL != (ChildDataSet = GetNextChildDataSet(DataSetCtx, ChildDataSet))) { RecurseDataSet(ChildDataSet, SquashDataSetCb, NULL, NewRootDataSet, TRUE); FreeAllKeyValuePairs(NewRootDataSet); } return NewRootDataSet; } /* * Helper to obtain a format string for the specific type */ CHAR16 * FormatString(KEY_TYPE KeyType, TO_STRING_BASE Base) { switch (KeyType) { case KEY_UINT64: return (Base == HEX) ? L"0x" FORMAT_UINT64_HEX : FORMAT_UINT64; case KEY_INT64: return (Base == HEX) ? L"0x" FORMAT_UINT64_HEX : FORMAT_INT64; case KEY_UINT32: return (Base == HEX) ? L"0x" FORMAT_UINT32_HEX : FORMAT_UINT32; case KEY_INT32: return (Base == HEX) ? L"0x" FORMAT_UINT32_HEX : FORMAT_INT32; case KEY_UINT16: return (Base == HEX) ? L"0x" FORMAT_UINT16_HEX : FORMAT_UINT16; case KEY_INT16: return (Base == HEX) ? L"0x" FORMAT_UINT16_HEX : FORMAT_INT16; case KEY_UINT8: return (Base == HEX) ? L"0x" FORMAT_UINT8_HEX : FORMAT_UINT8; case KEY_INT8: return (Base == HEX) ? L"0x" FORMAT_UINT8_HEX : FORMAT_INT8; default: return NULL; } } /* * Helper to locate a child node with a particular name */ KEY_VAL * FindKeyValuePair(DATA_SET *DataSet, const CHAR16 *Key) { LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; KEY_VAL *KeyVal; DATA_SET_LIST_FOR_EACH_SAFE(Entry, NextEntry, &DataSet->KeyValueList) { KeyVal = BASE_CR(Entry, KEY_VAL, Link); if (0 == StrCmp(Key, KeyVal->KeyValInfo.Key)) { return KeyVal; } } return NULL; } /* * Free a single KeyVal Struct */ VOID FreeKeyValMem(KEY_VAL *KeyVal) { if (NULL == KeyVal) { return; } if (KeyVal->KeyValInfo.Key) { FreePool(KeyVal->KeyValInfo.Key); } if ((KeyVal->ValueToString) && (KeyVal->ValueToString != KeyVal->Value)) { FreePool(KeyVal->ValueToString); } if (KeyVal->Value) { FreePool(KeyVal->Value); } if (KeyVal->KeyValInfo.UserData) { FreePool(KeyVal->KeyValInfo.UserData); } FreePool(KeyVal); } /* * Free all key val pairs in the set */ VOID FreeAllKeyValuePairs(DATA_SET *DataSet) { LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; KEY_VAL *KeyVal; DATA_SET_LIST_FOR_EACH_SAFE(Entry, NextEntry, &DataSet->KeyValueList) { KeyVal = BASE_CR(Entry, KEY_VAL, Link); RemoveEntryList(&KeyVal->Link); FreeKeyValMem(KeyVal); } } /* * Create a new keyval struct */ KEY_VAL * CreateKeyVal(DATA_SET_CONTEXT *DataSetCtx) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; KEY_VAL *KeyVal = (KEY_VAL*)AllocateZeroPool(sizeof(KEY_VAL)); if(NULL == KeyVal) { return NULL; } InsertTailList(&DataSet->KeyValueList, &KeyVal->Link); return KeyVal; } /* * Set a unicode string value */ EFI_STATUS EFIAPI SetKeyValueWideStrFormat(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, const CHAR16 *Val, ...) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; CHAR16 *TempMsg = NULL; VA_LIST Marker; EFI_STATUS RetCode = EFI_SUCCESS; if (NULL == Key || NULL == Val || NULL == DataSet) { return EFI_INVALID_PARAMETER; } VA_START(Marker, Val); TempMsg = CatVSPrint(NULL, Val, Marker); VA_END(Marker); RetCode = SetKeyValueWideStr(DataSetCtx, Key, TempMsg); FREE_POOL_SAFE(TempMsg); return RetCode; } /* * Set a unicode string value */ EFI_STATUS SetKeyValueWideStr(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, const CHAR16 *Val) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; KEY_VAL *KeyVal = NULL; if (NULL == Key || NULL == Val || NULL == DataSet) { return EFI_INVALID_PARAMETER; } //first try to find the key, but if not found create a new key/value entry //and set the name of the key if (NULL == (KeyVal = FindKeyValuePair(DataSet, Key))) { if (NULL == (KeyVal = CreateKeyVal(DataSet))) { return EFI_OUT_OF_RESOURCES; } KeyVal->KeyValInfo.Key = CatSPrint(NULL, Key); } //found the key, now free previous values (string and actual value) else { if ((KeyVal->ValueToString) && (KeyVal->ValueToString != KeyVal->Value)) { FreePool(KeyVal->ValueToString); } if (KeyVal->Value) { FreePool(KeyVal->Value); } } if (NULL == (KeyVal->Value = AllocatePool(StrSize(Val)))) { return EFI_OUT_OF_RESOURCES; } CopyMem(KeyVal->Value, (VOID*)Val, StrSize(Val)); KeyVal->ValueToString = KeyVal->Value; SetAncestorsDirty(DataSetCtx); return EFI_SUCCESS; } /* * Retrieve a unicode string from the data set. */ EFI_STATUS GetKeyValueWideStr(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, CHAR16 **Val, CHAR16 *DefaultVal) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; KEY_VAL *KeyVal = NULL; if (NULL == Key || NULL == Val || NULL == DataSet) { return EFI_INVALID_PARAMETER; } if (NULL == (KeyVal = FindKeyValuePair(DataSet, Key))) { *Val = DefaultVal; } else { *Val = KeyVal->ValueToString; } return EFI_SUCCESS; } /* * Helper to set all primitive types */ KEY_VAL * SetKeyValue(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, VOID * Val, UINTN ValSize) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; KEY_VAL *KeyVal = NULL; if (NULL == Key || NULL == Val || NULL == DataSet) { return NULL; } if (NULL == (KeyVal = FindKeyValuePair(DataSet, Key))) { if (NULL == (KeyVal = CreateKeyVal(DataSet))) { return NULL; } KeyVal->KeyValInfo.Key = CatSPrint(NULL, Key); } else { if ((KeyVal->ValueToString) && (KeyVal->ValueToString != KeyVal->Value)) { FreePool(KeyVal->ValueToString); } if (KeyVal->Value) { FreePool(KeyVal->Value); } } KeyVal->Value = AllocatePool(ValSize); if(KeyVal->Value) { CopyMem(KeyVal->Value, Val, ValSize); } KeyVal->KeyValInfo.ValueSize = (UINT32)ValSize; SetAncestorsDirty(DataSetCtx); return KeyVal; } /* * Helper to get all primitive types */ EFI_STATUS GetKeyValue(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, VOID *Val, UINTN ValSize, VOID *DefaultVal) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; KEY_VAL *KeyVal = NULL; if (NULL == Key || NULL == Val || NULL == DataSet) { return EFI_INVALID_PARAMETER; } if (NULL == (KeyVal = FindKeyValuePair(DataSet, Key))) { CopyMem(Val, DefaultVal, ValSize); } else { if(KeyVal->KeyValInfo.ValueSize <= ValSize) { CopyMem(Val, KeyVal->Value, KeyVal->KeyValInfo.ValueSize); } else { CopyMem(Val, DefaultVal, ValSize); } } return EFI_SUCCESS; } /* * Set a boolean into the data set. */ EFI_STATUS SetKeyValueBool(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, BOOLEAN Val) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; KEY_VAL *KeyVal = NULL; const CHAR16 *BoolVal = Val ? BOOL_TRUE_STR : BOOL_FALSE_STR; if (NULL == Key || NULL == DataSet) { return EFI_INVALID_PARAMETER; } if (NULL == (KeyVal = FindKeyValuePair(DataSet, Key))) { if (NULL == (KeyVal = CreateKeyVal(DataSet))) { return EFI_OUT_OF_RESOURCES; } KeyVal->KeyValInfo.Key = CatSPrint(NULL, Key); } else { if ((KeyVal->ValueToString) && (KeyVal->ValueToString != KeyVal->Value)) { FreePool(KeyVal->ValueToString); } if (KeyVal->Value) { FreePool(KeyVal->Value); } } if (NULL == (KeyVal->Value = AllocatePool(sizeof(BOOLEAN)))) { return EFI_OUT_OF_RESOURCES; } CopyMem(KeyVal->Value, (VOID*)&Val, sizeof(BOOLEAN)); KeyVal->ValueToString = CatSPrint(NULL, BoolVal); SetAncestorsDirty(DataSetCtx); return EFI_SUCCESS; } /* * Retrieve a boolean value from the data set. */ EFI_STATUS GetKeyValueBool(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, BOOLEAN *Val, BOOLEAN *DefaultVal) { return GetKeyValue(DataSetCtx, Key, (VOID *)Val, sizeof(BOOLEAN), (VOID *)DefaultVal); } /* * Set an unsigned 64 bit value into the data set. */ EFI_STATUS SetKeyValueUint64(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT64 Val, TO_STRING_BASE Base) { EFI_STATUS RetVal = EFI_SUCCESS; SET_KEY_VALUE(DataSetCtx, &RetVal, Key, (VOID*)&Val, UINT64, KEY_UINT64, Base); return RetVal; } /* * Retrieve an unsigned 64 bit value from the data set. */ EFI_STATUS GetKeyValueUint64(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT64 *Val, UINT64 *DefaultVal) { return GetKeyValue(DataSetCtx, Key, (VOID *)Val, sizeof(UINT64), (VOID *)DefaultVal); } /* * Set an signed 64 bit value into the data set. */ EFI_STATUS SetKeyValueInt64(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT64 Val, TO_STRING_BASE Base) { EFI_STATUS RetVal = EFI_SUCCESS; SET_KEY_VALUE(DataSetCtx, &RetVal, Key, (VOID*)&Val, INT64, KEY_INT64, Base); return RetVal; } /* * Retrieve a signed 64 bit value from the data set. */ EFI_STATUS GetKeyValueInt64(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT64 *Val, INT64 *DefaultVal) { return GetKeyValue(DataSetCtx, Key, (VOID *)Val, sizeof(INT64), (VOID *)DefaultVal); } /* * Set an unsigned 32 bit value into the data set. */ EFI_STATUS SetKeyValueUint32(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT32 Val, TO_STRING_BASE Base) { EFI_STATUS RetVal = EFI_SUCCESS; SET_KEY_VALUE(DataSetCtx, &RetVal, Key, (VOID*)&Val, UINT32, KEY_UINT32, Base); return RetVal; } /* * Retrieve an unsigned 32 bit value from the data set. */ EFI_STATUS GetKeyValueUint32(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT32 *Val, UINT32 *DefaultVal) { return GetKeyValue(DataSetCtx, Key, (VOID *)Val, sizeof(UINT32), (VOID *)DefaultVal); } /* * Set an signed 32 bit value into the data set. */ EFI_STATUS SetKeyValueInt32(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT32 Val, TO_STRING_BASE Base) { EFI_STATUS RetVal = EFI_SUCCESS; SET_KEY_VALUE(DataSetCtx, &RetVal, Key, (VOID*)&Val, INT32, KEY_INT32, Base); return RetVal; } /* * Retrieve a signed 32 bit value from the data set. */ EFI_STATUS GetKeyValueInt32(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT32 *Val, INT32 *DefaultVal) { return GetKeyValue(DataSetCtx, Key, (VOID *)Val, sizeof(INT32), (VOID *)DefaultVal); } /* * Set an unsigned 16 bit value into the data set. */ EFI_STATUS SetKeyValueUint16(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT16 Val, TO_STRING_BASE Base) { EFI_STATUS RetVal = EFI_SUCCESS; SET_KEY_VALUE(DataSetCtx, &RetVal, Key, (VOID*)&Val, UINT16, KEY_UINT16, Base); return RetVal; } /* * Retrieve an unsigned 16 bit value from the data set. */ EFI_STATUS GetKeyValueUint16(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT16 *Val, UINT16 *DefaultVal) { return GetKeyValue(DataSetCtx, Key, (VOID *)Val, sizeof(UINT16), (VOID *)DefaultVal); } /* * Set an signed 16 bit value into the data set. */ EFI_STATUS SetKeyValueInt16(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT16 Val, TO_STRING_BASE Base) { EFI_STATUS RetVal = EFI_SUCCESS; SET_KEY_VALUE(DataSetCtx, &RetVal, Key, (VOID*)&Val, INT16, KEY_INT16, Base); return RetVal; } /* * Retrieve a signed 16 bit value from the data set. */ EFI_STATUS GetKeyValueInt16(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT16 *Val, INT16 *DefaultVal) { return GetKeyValue(DataSetCtx, Key, (VOID *)Val, sizeof(INT16), (VOID *)DefaultVal); } /* * Set an unsigned 8 bit value into the data set. */ EFI_STATUS SetKeyValueUint8(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT8 Val, TO_STRING_BASE Base) { EFI_STATUS RetVal = EFI_SUCCESS; SET_KEY_VALUE(DataSetCtx, &RetVal, Key, (VOID*)&Val, UINT8, KEY_UINT8, Base); return RetVal; } /* * Retrieve an unsigned 8 bit value from the data set. */ EFI_STATUS GetKeyValueUint8(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT8 *Val, UINT8 *DefaultVal) { return GetKeyValue(DataSetCtx, Key, (VOID *)Val, sizeof(UINT8), (VOID *)DefaultVal); } /* * Set a signed 8 bit value into the data set. */ EFI_STATUS SetKeyValueInt8(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT8 Val, TO_STRING_BASE Base) { EFI_STATUS RetVal = EFI_SUCCESS; SET_KEY_VALUE(DataSetCtx, &RetVal, Key, (VOID*)&Val, INT8, KEY_INT8, Base); return RetVal; } /* * Retrieve a signed 8 bit value from the data set. */ EFI_STATUS GetKeyValueInt8(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT8 *Val, INT8 *DefaultVal) { return GetKeyValue(DataSetCtx, Key, (VOID *)Val, sizeof(INT8), (VOID *)DefaultVal); } /* * Get the next key in the data set. KeyInfo == NULL retrieves the first key in the data set. */ KEY_VAL_INFO * GetNextKey(DATA_SET_CONTEXT *DataSetCtx, KEY_VAL_INFO *KeyInfo) { LIST_ENTRY *Entry; KEY_VAL *KeyVal; DATA_SET *DataSet = (DATA_SET *)DataSetCtx; if (NULL == KeyInfo) { //GetFirstNode returns original list is empty if (&DataSet->KeyValueList == (Entry = GetFirstNode(&DataSet->KeyValueList))) { return NULL; } KeyVal = BASE_CR(Entry, KEY_VAL, Link); return &KeyVal->KeyValInfo; } if (NULL != (KeyVal = FindKeyValuePair(DataSet, KeyInfo->Key))) { if (NULL != (Entry = GetNextNode(&DataSet->KeyValueList, &KeyVal->Link))) { //GetNextNode returns original list when Link is the last node in list. if (Entry != &DataSet->KeyValueList) { KeyVal = BASE_CR(Entry, KEY_VAL, Link); return &KeyVal->KeyValInfo; } } } return NULL; } /* * Get the number of key/val pairs in a data set. */ UINT32 GetKeyCount(DATA_SET_CONTEXT *DataSetCtx) { KEY_VAL_INFO * pKeyInfo = NULL; UINT32 Count = 0; while (NULL != (pKeyInfo = GetNextKey(DataSetCtx, pKeyInfo))) { ++Count; } return Count; } /* * Associate user data with a particular key */ EFI_STATUS SetKeyUserData(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, VOID *UserData) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; KEY_VAL *KeyVal = NULL; if (NULL == Key || NULL == DataSet || NULL == UserData) { return EFI_INVALID_PARAMETER; } if (NULL != (KeyVal = FindKeyValuePair(DataSet, Key))) { KeyVal->KeyValInfo.UserData = UserData; } else { return EFI_NOT_FOUND; } return EFI_SUCCESS; } /* * Retrieve user data from a particular key */ VOID * GetKeyUserData(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key) { DATA_SET *DataSet = (DATA_SET*)DataSetCtx; KEY_VAL *KeyVal = NULL; if (NULL == Key || NULL == DataSet) { return NULL; } if (NULL != (KeyVal = FindKeyValuePair(DataSet, Key))) { return KeyVal->KeyValInfo.UserData; } else { return NULL; } } ipmctl-03.00.00.0485/DcpmPkg/common/DataSet.h000066400000000000000000000172751440615110200201420ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DATA_SET_H_ #define _DATA_SET_H_ #include #include #include #define PATH_KEY_DELIM L"." #define DIMM_LIST_NODE_STR L"DimmList" #define DIMM_NODE_STR L"Dimm" #define SENSOR_LIST_NODE_STR L"SensorList" #define SENSOR_NODE_STR L"Sensor" #define PREFERENCES_NODE_STR L"Preferences" #define SOCKET_NODE_STR L"Socket" #define SOCKET_LIST_NODE_STR L"SocketList" #define REGION_NODE_STR L"Region" #define REGION_LIST_NODE_STR L"RegionList" #define CONFIG_GOAL_NODE_STR L"ConfigGoal" #define TOPOLOGY_NODE_STR L"DimmTopology" #define DIAGNOSTIC_NODE_STR L"Diagnostic" #define NAMESPACE_NODE_STR L"Namespace" #define ACPI_MODE_STR L"Acpi" #define ACPITYPE_MODE_STR L"Type" #define REGISTER_MODE_STR L"Register" #define SMBIOS_MODE_STR L"Smbios" #define DIE_NODE_STR L"Die" #define IMC_NODE_STR L"Imc" #define CHANNEL_NODE_STR L"Channel" #define SLOT_NODE_STR L"Slot" /* * Types of values supported in DataSets. */ typedef enum { KEY_W_STR, KEY_UINT64, KEY_INT64, KEY_UINT32, KEY_INT32, KEY_UINT16, KEY_INT16, KEY_UINT8, KEY_INT8, KEY_BOOL } KEY_TYPE; /* * Types of ToString */ typedef enum { HEX, DECIMAL } TO_STRING_BASE; /* * Information describing a key/value pair in a DataSet. */ typedef struct _KEY_VAL_INFO { KEY_TYPE Type; //type of value associated with a particular key CHAR16 *Key; //the name associated with a particular value UINT32 ValueSize; //the binary size of the value VOID *UserData; //user data }KEY_VAL_INFO; #define DATA_SET_CONTEXT VOID /* * Utilized with DataSet recursing APIs. Executed for each DataSet found while traversing * a DataSet tree. */ typedef VOID * (*DataSetCallBack)(DATA_SET_CONTEXT *, CHAR16*, VOID*, VOID*); /* * Utilized with DataSet recursing APIs. Executed when all children DataSets have been traversed. */ typedef VOID * (*DataSetAllChildrenDoneCallBack)(DATA_SET_CONTEXT *, CHAR16*, VOID*); /* * Create a new data set structure */ DATA_SET_CONTEXT *CreateDataSet(DATA_SET_CONTEXT *DataSetCtx, CHAR16 *Name, VOID *UserData); /* * Get the next child dataset in the data set. */ DATA_SET_CONTEXT *GetNextChildDataSet(DATA_SET_CONTEXT *DataSetCtx, DATA_SET_CONTEXT *CurrentChildDataSetCtx); /* * Does the data set contain children data sets? */ BOOLEAN IsLeaf(DATA_SET_CONTEXT *DataSetCtx); /* * Free a data set structure */ VOID FreeDataSet(DATA_SET_CONTEXT *DataSetCtx); /* * Retrieve a data set by specifying a path in the form of /sensorlist/dimm[0]/sensor[1] */ DATA_SET_CONTEXT * EFIAPI GetDataSet(DATA_SET_CONTEXT *Root, CHAR16 *NamePath, ...); /* * Get the name of a data set */ CHAR16 * GetDataSetName(DATA_SET_CONTEXT *DataSetCtx); /* * set the user's data to the corresponding data set (UserData must be allocated by AllocatePool) */ VOID SetDataSetName(DATA_SET_CONTEXT *DataSetCtx, CHAR16 *Name); /* * Return the user's data from the corresponding data set */ VOID * GetDataSetUserData(DATA_SET_CONTEXT *DataSetCtx); /* * set the user's data to the corresponding data set (UserData must be allocated by AllocatePool) */ VOID SetDataSetUserData(DATA_SET_CONTEXT *DataSetCtx, VOID *UserData); /* * Recurses through data set tree, invoking the callback at each node (data set) in the tree */ VOID RecurseDataSet(DATA_SET_CONTEXT *DataSetCtx, DataSetCallBack CallBackRoutine, DataSetAllChildrenDoneCallBack ChildrenDoneCallBackRoutine, VOID *, BOOLEAN Sparse); /* * Callback routine for squash operation */ DATA_SET_CONTEXT *SquashDataSet(DATA_SET_CONTEXT *DataSetCtx); /* * Get the next key in the data set. KeyInfo == NULL retrieves the first key in the data set. */ KEY_VAL_INFO * GetNextKey(DATA_SET_CONTEXT *DataSetCtx, KEY_VAL_INFO *KeyInfo); /* * Get the number of key/val pairs in a data set. */ UINT32 GetKeyCount(DATA_SET_CONTEXT *DataSetCtx); /* * Set a unicode string value */ EFI_STATUS SetKeyValueWideStr(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, const CHAR16 *Val); /* * Set a unicode string value, where Val is a format string */ EFI_STATUS EFIAPI SetKeyValueWideStrFormat(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, const CHAR16 *Val, ...); /* * Set an unsigned 64 bit value into the data set. */ EFI_STATUS SetKeyValueUint64(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT64 Val, TO_STRING_BASE Base); /* * Retrieve an unsigned 64 bit value from the data set. */ EFI_STATUS SetKeyValueInt64(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT64 Val, TO_STRING_BASE Base); /* * Set an unsigned 32 bit value into the data set. */ EFI_STATUS SetKeyValueUint32(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT32 Val, TO_STRING_BASE Base); /* * Set an signed 32 bit value into the data set. */ EFI_STATUS SetKeyValueInt32(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT32 Val, TO_STRING_BASE Base); /* * Set an unsigned 16 bit value into the data set. */ EFI_STATUS SetKeyValueUint16(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT16 Val, TO_STRING_BASE Base); /* * Set an signed 16 bit value into the data set. */ EFI_STATUS SetKeyValueInt16(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT16 Val, TO_STRING_BASE Base); /* * Set a boolean into the data set. */ EFI_STATUS SetKeyValueBool(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, BOOLEAN Val); /* * Set an unsigned 8 bit value into the data set. */ EFI_STATUS SetKeyValueUint8(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT8 Val, TO_STRING_BASE Base); /* * Set a signed 8 bit value into the data set. */ EFI_STATUS SetKeyValueInt8(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT8 Val, TO_STRING_BASE Base); /* * Retrieve a unicode string from the data set. */ EFI_STATUS GetKeyValueWideStr(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, CHAR16 **Val, CHAR16 *DefaultVal); /* * Retrieve an unsigned 64 bit value from the data set. */ EFI_STATUS GetKeyValueUint64(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT64 *Val, UINT64 *DefaultVal); /* * Retrieve an signed 64 bit value into the data set. */ EFI_STATUS GetKeyValueInt64(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT64 *Val, INT64 *DefaultVal); /* * Retrieve an unsigned 32 bit value from the data set. */ EFI_STATUS GetKeyValueUint32(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT32 *Val, UINT32 *DefaultVal); /* * Retrieve a signed 32 bit value from the data set. */ EFI_STATUS GetKeyValueInt32(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT32 *Val, INT32 *DefaultVal); /* * Retrieve an unsigned 16 bit value from the data set. */ EFI_STATUS GetKeyValueUint16(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT16 *Val, UINT16 *DefaultVal); /* * Retrieve a signed 16 bit value from the data set. */ EFI_STATUS GetKeyValueInt16(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT16 *Val, INT16 *DefaultVal); /* * Retrieve a boolean value from the data set. */ EFI_STATUS GetKeyValueBool(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, BOOLEAN *Val, BOOLEAN *DefaultVal); /* * Set an unsigned 8 bit value into the data set. */ EFI_STATUS GetKeyValueUint8(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, UINT8 *Val, UINT8 *DefaultVal); /* * Retrieve a signed 8 bit value from the data set. */ EFI_STATUS GetKeyValueInt8(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, INT8 *Val, INT8 *DefaultVal); /* * Associate user data with a particular key */ EFI_STATUS SetKeyUserData(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key, VOID *UserData); /* * Retrieve user data from a particular key */ VOID * GetKeyUserData(DATA_SET_CONTEXT *DataSetCtx, const CHAR16 *Key); #define FREE_DATASET_RECURSIVE_SAFE(DataSet) { \ if (DataSet != NULL) { \ FreeDataSet(DataSet); \ DataSet = NULL; \ } \ }; #endif /** _DATA_SET_H_**/ ipmctl-03.00.00.0485/DcpmPkg/common/Debug.h000066400000000000000000000255551440615110200176430ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DEBUG_H_ #define _DEBUG_H_ #include #include #include #include "Utility.h" #ifdef OS_BUILD #include #endif #define G_FN_NAME_SIZE 1024 extern EFI_BOOT_SERVICES *gBS; extern CHAR16 gFnName[G_FN_NAME_SIZE]; #define WIDEN2(x) L ## x #define WIDEN(x) WIDEN2(x) #define __WFUNCTION__ \ ((AsciiStrToUnicodeStrS(__FUNCTION__, gFnName, G_FN_NAME_SIZE) == RETURN_SUCCESS) ? gFnName : L"" ) #ifdef OS_BUILD extern UINTN EFIAPI PrintNoBuffer(CHAR16* fmt, ...); #endif #ifndef OS_BUILD #define NVDIMM_BUFFER_CONTROLLED_MSG(Buffered, Format, ...) \ Print(Format, ## __VA_ARGS__) #else #define NVDIMM_BUFFER_CONTROLLED_MSG(Buffered, Format, ...) \ do \ { \ if (!Buffered) \ PrintNoBuffer(Format, ## __VA_ARGS__); \ else Print(Format, ## __VA_ARGS__); \ } while (0) #endif #ifdef _MSC_VER #define SUB_DIR_CHAR '\\' #else // MSVC #define SUB_DIR_CHAR '/' #endif // MSVC #ifdef OS_BUILD static INLINE CONST CHAR8 *FileFromPath(CONST CHAR8 *path) { int i = 0; int index = 0; while (path[i] != '\0') { if (path[i] == SUB_DIR_CHAR) { index = i; } i++; } return path + index + 1; } #else // !OS_BUILD static INLINE CONST CHAR16 *FileFromPath(CONST CHAR16 *path) { int i = 0; int index = 0; while (path[i] != L'\0') { if (path[i] == SUB_DIR_CHAR) { index = i; } i++; } return path + index + 1; } #endif // OS_BUILD #define __WFILE__ WIDEN(__FILE__) #ifndef MDEPKG_NDEBUG #define SR_BIOS_SERIAL_DEBUG_CSR_OFF 0xb8 #define SR_BIOS_SERIAL_DEBUG_CSR_BUS 0x0 #define SR_BIOS_SERIAL_DEBUG_CSR_DEV 0x8 #define SR_BIOS_SERIAL_DEBUG_CSR_FUN 0x2 #ifdef OS_BUILD //FIXME: Added for GCC build on linux #ifdef DEBUG_BUILD #if defined(_MSC_VER) || defined(__GNUC__) #define NVDIMM_ENTRY() \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Entering %s::%s()\n", \ FileFromPath(__FILE__), __FUNCTION__) #define NVDIMM_EXIT() \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s()\n", \ FileFromPath(__FILE__), __FUNCTION__) #define NVDIMM_EXIT_I(rc) \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s(): 0x%x\n", \ FileFromPath(__FILE__), __FUNCTION__, rc) #define NVDIMM_EXIT_I64(rc) \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s(): 0x%x\n", \ FileFromPath(__FILE__), __FUNCTION__, rc) #define NVDIMM_EXIT_CHECK_I64(rc) \ if(rc) { \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s(): 0x%x\n", \ FileFromPath(__FILE__), __FUNCTION__, rc); \ } #else #define NVDIMM_ENTRY() \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Entering %s::%s()\n", \ FileFromPath(__FILE__), __FUNCTION__); \ RegisterStackTrace((FileFromPath(__FILE__)), (__FUNCTION__)) #define NVDIMM_EXIT() \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s()\n", \ FileFromPath(__FILE__), __FUNCTION__); \ PopStackTrace() #define NVDIMM_EXIT_I(rc) \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s(): 0x%x\n", \ FileFromPath(__FILE__), __FUNCTION__, rc); \ PopStackTrace() #define NVDIMM_EXIT_I64(rc) \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s(): 0x%x\n", \ FileFromPath(__FILE__), __FUNCTION__, rc); \ PopStackTrace() #define NVDIMM_EXIT_CHECK_I64(rc) \ if(rc) { \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s(): 0x%x\n", \ FileFromPath(__FILE__), __FUNCTION__, rc); \ } \ PopStackTrace() #endif #else // DEBUG_BUILD #define NVDIMM_ENTRY() #define NVDIMM_EXIT() #define NVDIMM_EXIT_I(rc) #define NVDIMM_EXIT_I64(rc) #define NVDIMM_EXIT_CHECK_I64(rc) #endif // DEBUG_BUILD /** Error level messages are messages that tell the user what happened in the case of a critical failure. They should be general enough for the end user and only show if the CLI command fails (not all failures have these debug messages). These are usually something the user can fix or the message they will get just before the system shuts down. Warning level debug messages are shown when something fails that may or may not cause issues to the user. Messages should be understood without knowledge of the codebase. Something like NFIT table issues, BIOS issues, no DIMMs, failed to initialize smbus, failed to retrieve pools, EFI return codes, invalid CLI parameters. Debug level messages are messages that may be helpful to debug an issue, such as the value of variables or status, or something specific failed. These are more specific to the codebase. Verbose/info messages typically show where we are in the code, such as method entry/exit. **/ #define NVDIMM_VERB(fmt, ...) \ OS_NVDIMM_VERB(fmt, ## __VA_ARGS__) /* DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:%s::%s:%d: " fmt "\n", \ FileFromPath(__FILE__), __FUNCTION__, __LINE__, ## __VA_ARGS__)*/ #define NVDIMM_DBG(fmt, ...) \ OS_NVDIMM_DBG(fmt, ## __VA_ARGS__) #define NVDIMM_DBG_CLEAN(fmt, ...) \ OS_NVDIMM_DBG_CLEAN(fmt, ## __VA_ARGS__) #define NVDIMM_WARN(fmt, ...) \ OS_NVDIMM_WARN(fmt, ## __VA_ARGS__) #define NVDIMM_ERR(fmt, ...) \ OS_NVDIMM_ERR(fmt, ## __VA_ARGS__) #define NVDIMM_ERR_W(fmt, ...) /*\ DebugPrint(EFI_D_ERROR, "NVDIMM-ERR:%s::%s:%d: " fmt "\n", \ FileFromPath(__WFILE__), __WFUNCTION__, __LINE__, ## __VA_ARGS__)*/ #define NVDIMM_CRIT(fmt, ...) \ OS_NVDIMM_CRIT(fmt, ## __VA_ARGS__) #define NVDIMM_HEXDUMP(data, size) \ HexDebugPrint(data, size) #else // OS_BUILD //FIXME: Added for GCC build on linux #if defined(_MSC_VER) || defined(__GNUC__) #define NVDIMM_ENTRY() \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Entering %s::%s()\n", \ FileFromPath(__WFILE__), __WFUNCTION__) #define NVDIMM_EXIT() \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s()\n", \ FileFromPath(__WFILE__), __WFUNCTION__) #define NVDIMM_EXIT_I(rc) \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s(): 0x%x\n", \ FileFromPath(__WFILE__), __WFUNCTION__, rc) #define NVDIMM_EXIT_I64(rc) \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s(): " FORMAT_EFI_STATUS "\n", \ FileFromPath(__WFILE__), __WFUNCTION__, rc) #define NVDIMM_EXIT_CHECK_I64(rc) \ if(rc) { \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s(): 0x%x\n", \ FileFromPath(__WFILE__), __WFUNCTION__, rc); \ } #else #define NVDIMM_ENTRY() \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Entering %s::%s()\n", \ FileFromPath(__WFILE__), __WFUNCTION__); \ RegisterStackTrace((FileFromPath(__WFILE__)), (__WFUNCTION__)) #define NVDIMM_EXIT() \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s()\n", \ FileFromPath(__WFILE__), __WFUNCTION__); \ PopStackTrace() #define NVDIMM_EXIT_I(rc) \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s(): 0x%x\n", \ FileFromPath(__WFILE__), __WFUNCTION__, rc); \ PopStackTrace() #define NVDIMM_EXIT_I64(rc) \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s(): " FORMAT_EFI_STATUS "\n", \ FileFromPath(__WFILE__), __WFUNCTION__, rc); \ PopStackTrace() #define NVDIMM_EXIT_CHECK_I64(rc) \ if(rc) { \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:Exiting %s::%s(): 0x%x\n", \ FileFromPath(__WFILE__), __WFUNCTION__, rc); \ } PopStackTrace() #endif /** Error level messages are messages that tell the user what happened in the case of a critical failure. They should be general enough for the end user and only show if the CLI command fails (not all failures have these debug messages). These are usually something the user can fix or the message they will get just before the system shuts down. Warning level debug messages are shown when something fails that may or may not cause issues to the user. Messages should be understood without knowledge of the codebase. Something like NFIT table issues, BIOS issues, no DIMMs, failed to initialize smbus, failed to retrieve pools, EFI return codes, invalid CLI parameters. Debug level messages are messages that may be helpful to debug an issue, such as the value of variables or status, or something specific failed. These are more specific to the codebase. Verbose/info messages typically show where we are in the code, such as method entry/exit. **/ #define NVDIMM_VERB(fmt, ...) \ DebugPrint(EFI_D_VERBOSE, "NVDIMM-VERB:%s::%s:%d: " fmt "\n", \ FileFromPath(__WFILE__), __WFUNCTION__, __LINE__, ## __VA_ARGS__) #define NVDIMM_DBG(fmt, ...) \ DebugPrint(EFI_D_INFO, "NVDIMM-DBG:%s::%s:%d: " fmt "\n", \ FileFromPath(__WFILE__), __WFUNCTION__, __LINE__, ## __VA_ARGS__) #define NVDIMM_DBG_CLEAN(fmt, ...) \ DebugPrint(EFI_D_INFO, fmt, ## __VA_ARGS__) #define NVDIMM_WARN(fmt, ...) \ DebugPrint(EFI_D_WARN, "NVDIMM-WARN:%s::%s:%d: " fmt "\n", \ FileFromPath(__WFILE__), __WFUNCTION__, __LINE__, ## __VA_ARGS__) #define NVDIMM_ERR(fmt, ...) \ DebugPrint(EFI_D_ERROR, "NVDIMM-ERR:%s::%s:%d: " fmt "\n", \ FileFromPath(__WFILE__), __WFUNCTION__, __LINE__, ## __VA_ARGS__) #define NVDIMM_ERR_W(fmt, ...) /*\ DebugPrint(EFI_D_ERROR, "NVDIMM-ERR:%s::%s:%d: " fmt "\n", \ FileFromPath(__WFILE__), __WFUNCTION__, __LINE__, ## __VA_ARGS__)*/ #define NVDIMM_CRIT(fmt, ...) \ DebugPrint(EFI_D_ERROR, "NVDIMM-ERR:%s::%s:%d: " fmt "\n", \ FileFromPath(__WFILE__), __WFUNCTION__, __LINE__, ## __VA_ARGS__) #define NVDIMM_HEXDUMP(data, size) \ HexDebugPrint(data, size) #endif //OS_BUILD #define CREATE_COMPARE_CODE(MajorCode, MinorCode) (((MajorCode) << 8 | (MinorCode)) << 16) STATIC INLINE VOID OutputCheckpoint( IN UINT8 MajorCode, IN UINT8 MinorCode ) { CONST UINT32 CompareCode = CREATE_COMPARE_CODE(MajorCode, MinorCode); CONST UINT32 ScratchPadRegAddr = PCI_LIB_ADDRESS(SR_BIOS_SERIAL_DEBUG_CSR_BUS, SR_BIOS_SERIAL_DEBUG_CSR_DEV, SR_BIOS_SERIAL_DEBUG_CSR_FUN, SR_BIOS_SERIAL_DEBUG_CSR_OFF); UINT32 ScratchPadReg = 0; ScratchPadReg = PciRead32(ScratchPadRegAddr); do { NVDIMM_DBG("Match checkpoint = %x, MajorCode = %x, MinorCode = %x", ScratchPadReg, MajorCode, MinorCode); gBS->Stall(1000); ScratchPadReg = PciRead32(ScratchPadRegAddr); } while ((ScratchPadReg & 0xFFFF0000) == CompareCode); } #else #define NVDIMM_ENTRY() #define NVDIMM_EXIT() #define NVDIMM_EXIT_I(rc) #define NVDIMM_EXIT_I64(rc) #define NVDIMM_EXIT_CHECK_I64(rc) #define NVDIMM_VERB(fmt, ...) #define NVDIMM_DBG(fmt, ...) #define NVDIMM_DBG_CLEAN(fmt, ...) #define NVDIMM_WARN(fmt, ...) #define NVDIMM_ERR(fmt, ...) #define NVDIMM_CRIT(fmt, ...) #define NVDIMM_HEXDUMP(data, size) STATIC INLINE VOID OutputCheckpoint( IN UINT8 MajorCode, IN UINT8 MinorCode ) { /** In release this function will be cut by compiler **/ } #endif /** MDEPKG_NDEBUG **/ /** Compile time assert: used for ensuring that dependencies between constant symbols values (enums, defines) meet certain condition. If assertion fail file won't compile and will throw gcc error: error: size of array __C_ASSERT__ is negative **/ #ifndef C_ASSERT #define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] #endif /** C_ASSERT **/ #endif /** _DEBUG_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/FormatStrings.h000066400000000000000000000205471440615110200214130ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _FORMAT_STRINGS_H_ #define _FORMAT_STRINGS_H_ #define FORMAT_NULL_CHAR L'\0' #ifndef OS_BUILD #define FORMAT_NL L"\n" #define FORMAT_STR L"%s" #define FORMAT_STR_WITH_PARENTHESIS L" (%s)" #define FORMAT_STR_SINGLE_QUOTE L"'%ls'" #define FORMAT_DYN_STR L"%*s" #define FORMAT_STR_NL L"%s\n" #define FORMAT_NL_STR L"\n%s" #define FORMAT_STR_NL_NL L"%s\n\n" #define FORMAT_NL_NL_STR L"\n\n%s" #define FORMAT_SPACE_STR L" %s" #define FORMAT_STR_SPACE L"%s " #define SPACE_FORMAT_STR_SPACE L" %s " #define FORMAT_SPACE_SPACE_SPACE_STR_EQ_STR_NL L" %s=%s\n" #define FORMAT_STR_EQ_DEC_NL L"%s=%d\n" #define FORMAT_3SPACE_STR_EQ_DEC_NL L" %s=%d\n" #define FORMAT_3SPACE_EQ_0X04HEX_NL L" %s=0x%04x\n" #define FORMAT_6SPACE_STR_EQ L" %s=" #define FORMAT_STR_COLON_SPACE_STR L"%s: %s" #define FORMAT_STR_COLON_SPACE_HEX L"%ls: 0x%x" #define FORMAT_SHOW_DIMM_HEADER L"%-22s%-14s%-14s%-16s%s\n" #define FORMAT_SHOW_SENSOR_HEADER L"%-21s %24s %19s %12s\n" #define FORMAT_SHOW_DIMM_MANU_DATE L"%02x-%02x" #define FORMAT_SHOW_TOPO L"%-21s %-12s %-10s 0x%04x %-13s\n" #define FORMAT_FW_UPDATE_HEADER L"%-18s %-11s %-11s\n" #define FORMAT_FW_UPDATE_VERSION L"%-22s%02d.%02d.%02d.%04d " #define FORMAT_SHOW_GOAL_HEADER L"%-10s %-21s %-12s %-15s %-15s\n" #define FORMAT_SHOW_GOAL_SINGLE L"0x%04x %-21s %-12s %-15s %-15s\n" #define FORMAT_SHOW_NS_HEADER L"%-11s %-11s %-8s\n" #define FORMAT_SHOW_NS_HEALTH L"0x%04x %-11s " #define FORMAT_SHOW_TOPO_HEADER L"%-21s %-12s %-10s %-11s %-13s\n" #define FORMAT_SHOW_SOCKET_HEADER L"%-9s %-22s %-20s\n" #define FORMAT_SHOW_SOCKET L"0x%04x %-22s %-20s\n" #define FORMAT_SHOW_SOCKET_MEM L"%-22s" #define FORMAT_SHOW_SOCKET_TOTAL_MEM L"%-20s" #define FORMAT_SHOW_REGION_HEADER L"%8s %8s %23s %9s %12s %11s\n" #define FORMAT_SHOW_REGION_ID L"%04d" #define FORMAT_SHOW_ISET_ID L"0x%016llx" #define FORMAT_16STR L"%16s" #define FORMAT_INT64 L"%ld" #define FORMAT_UINT64 L"%llu" #define FORMAT_UINT64_HEX L"%016lx" #define FORMAT_UINT64_HEX_NOWIDTH L"0x%llx" #define FORMAT_INT64 L"%ld" #define FORMAT_UINT32 L"%u" #define FORMAT_UINT32_HEX L"%08x" #define FORMAT_INT32 L"%d" #define FORMAT_UINT16 L"%u" #define FORMAT_UINT16_HEX L"%04x" #define FORMAT_INT16 L"%d" #define FORMAT_UINT8 L"%u" #define FORMAT_UINT8_HEX L"%02x" #define FORMAT_INT8 L"%d" #define FORMAT_CHAR L"%c" #define FORMAT_STR_WITH_COMMA L", %s" #define FORMAT_HEX_WITH_COMMA L", 0x%04x" #define FORMAT_HEX L"0x%04x" #define FORMAT_HEX_WITH_PARENTHESIS L" (0x%04x)" #define FORMAT_HEX_NOWIDTH L"0x%x" #define SPACE_FORMAT_HEX L" 0x%04x" #define FORMAT_EFI_STATUS "0x%lx" #define FORMAT_GUID L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" #define FORMAT_STEPPING L"%s%d, 0x%04x" #define FORMAT_HEX_PREFIX L"0x" #define FORMAT_DYNAMIC_WIDTH_LEADING_ZEROS L"%0*d" #define FORMAT_POINTER L"0x%x" #else // OS_BUILD #define FORMAT_NL L"\n" #define FORMAT_STR L"%ls" #define FORMAT_STR_WITH_PARENTHESIS L" (%ls)" #define FORMAT_DYN_STR L"%*ls" #define FORMAT_STR_NL L"%ls\n" #define FORMAT_NL_STR L"\n%ls" #define FORMAT_STR_NL_NL L"%ls\n\n" #define FORMAT_SPACE_STR L" %ls" #define FORMAT_STR_SPACE L"%ls " #define SPACE_FORMAT_STR_SPACE L" %ls " #define FORMAT_SPACE_SPACE_SPACE_STR_EQ_STR_NL L" %ls=%ls\n" #define FORMAT_STR_EQ_DEC_NL L"%ls=%d\n" #define FORMAT_STR_EQ_DEC_NL_NL L"%ls=%d\n\n" #define FORMAT_3SPACE_STR_NL L" %ls\n" #define FORMAT_3SPACE_STR_EQ_DEC_NL L" %ls=%d\n" #define FORMAT_3SPACE_EQ_0X04HEX_NL L" %ls=0x%04x\n" #define FORMAT_6SPACE_STR_EQ L" %ls=" #define FORMAT_STR_COLON_SPACE_STR L"%ls: %ls" #define FORMAT_STR_COLON_SPACE_HEX L"%ls: 0x%x" #define FORMAT_SHOW_DIMM_HEADER L"%ls\t%ls\t%ls\t%ls\t%ls\t%ls\n" #define FORMAT_SHOW_DIMM_CONTENT L"%ls\t%ls\t%ls\t\t%ls\t\t%ls\t%ls\n" #define FORMAT_SHOW_DIMM_MANU_DATE L"%02x-%02x" #define FORMAT_DEC L"%d" #define FORMAT_SHOW_SENSOR_HEADER L"%-21ls %24ls %19ls %12ls\n" #define FORMAT_SHOW_TOPO L"%ls\t%ls\t\t%ls\t0x%04x\t\t%ls\n" #define FORMAT_FW_UPDATE_HEADER L"%-18ls %-11ls %-11ls\n" #define FORMAT_FW_UPDATE_VERSION L"%-22ls%02d.%02d.%02d.%04d " #define FORMAT_SHOW_GOAL_HEADER L"%ls\t%ls\t%ls\t%ls\t%ls\n" #define FORMAT_SHOW_GOAL_SINGLE L"0x%04x\t\t%ls\t%ls\t\t%ls\t%ls\n" #define FORMAT_SHOW_NS_HEADER L"%-11ls %-11ls %-8ls\n" #define FORMAT_SHOW_NS_HEALTH L"0x%04x %-11ls " #define FORMAT_SHOW_TOPO_HEADER L"%ls\t%ls\t%ls\t%ls\t%ls\n" #define FORMAT_SHOW_SOCKET_HEADER L"%-9ls %-22ls %-20ls\n" #define FORMAT_SHOW_SOCKET L"0x%04x %-22ls %-20ls\n" #define FORMAT_SHOW_SOCKET_MEM L"%-22ls" #define FORMAT_SHOW_SOCKET_TOTAL_MEM L"%-20ls" #define FORMAT_SHOW_REGION_HEADER L"%8ls %18ls %23ls %9ls %12ls %11ls\n" #define FORMAT_SHOW_REGION_ID L"%04d" #define FORMAT_SHOW_ISET_ID L"0x%016llx" #define FORMAT_16STR L"%16ls" #define FORMAT_INT64 L"%lld" #define FORMAT_UINT64 L"%llu" #define FORMAT_UINT64_HEX L"%016llx" #define FORMAT_UINT64_HEX_NOWIDTH L"0x%llx" #define FORMAT_UINT32 L"%u" #define FORMAT_UINT32_HEX L"%08x" #define FORMAT_INT32 L"%d" #define FORMAT_UINT16 L"%hu" #define FORMAT_UINT16_HEX L"%04x" #define FORMAT_INT16 L"%hi" #define FORMAT_UINT8 L"%hhu" #define FORMAT_UINT8_HEX L"%02x" #define FORMAT_INT8 L"%hhi" #define FORMAT_STR_SINGLE_QUOTE L"'%ls'" #define FORMAT_CHAR L"%lc" #define FORMAT_STR_WITH_COMMA L", %ls" #define FORMAT_HEX_WITH_COMMA L", 0x%04x" #define FORMAT_HEX L"0x%04x" #define FORMAT_HEX_WITH_PARENTHESIS L" (0x%04x)" #define FORMAT_HEX_NOWIDTH L"0x%x" #define SPACE_FORMAT_HEX L" 0x%04x" #define FORMAT_EFI_STATUS "0x%x" #define FORMAT_GUID L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" #define FORMAT_STEPPING L"%ls%d, 0x%04x" #define FORMAT_HEX_PREFIX L"0x" #define FORMAT_DYNAMIC_WIDTH_LEADING_ZEROS L"%0*d" #define FORMAT_POINTER L"%p" #endif // OS_BUILD #endif /** _FORMAT_STRINGS_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/FwUtility.c000066400000000000000000000324231440615110200205400ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include "FwUtility.h" #include "Utility.h" #include #include "Debug.h" #include "Version.h" /** Checks if the firmware image is valid for an update. Please note that even if this function returns TRUE, the image may not be fully valid. There are additional security and CRC checks made by the DIMM that may fail. @param[in] pImage is the buffer that contains the image we want to validate. @param[in] ImageSize is the size in bytes of the valid image data in the buffer. The buffer must be bigger or equal to the ImageSize. @param[in] FWImageMaxSize is the maximum allowed size in bytes of the image. @param[out] pCommandStatus structure containing detailed NVM error codes @retval TRUE if the Image is valid for the update. @retval FALSE if the Image is not valid. **/ BOOLEAN ValidateImage( IN NVM_FW_IMAGE_HEADER *pImage, IN UINT64 ImageSize, IN UINT64 FWImageMaxSize, OUT COMMAND_STATUS *pCommandStatus ) { if (ImageSize > FWImageMaxSize || ImageSize < sizeof(NVM_FW_IMAGE_HEADER)) { CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_WRONG_IMAGE_SIZE); return FALSE; } if (ImageSize % UPDATE_FIRMWARE_SMALL_PAYLOAD_DATA_PACKET_SIZE != 0) { CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_IMAGE_NOT_ALIGNED); NVDIMM_DBG("The buffer size is not aligned to %d bytes.\n", UPDATE_FIRMWARE_SMALL_PAYLOAD_DATA_PACKET_SIZE); return FALSE; } if (pImage->ModuleVendor != VENDOR_ID) { CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_VENDOR_NOT_COMPATIBLE); return FALSE; } if (pImage->ModuleType != LT_MODULE_TYPE_CSS) { CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_MODULE_TYPE_NOT_COMPATIBLE); return FALSE; } return TRUE; } /** Checks if the firmware image is valid for an recovery over Spi. Please note that even if this function returns TRUE, the image may not be fully valid. There are additional security and CRC checks made by the DIMM that may fail. @param[in] pImage is the buffer that contains the image we want to validate. @param[in] ImageSize is the size in bytes of the valid image data in the buffer. The buffer must be bigger or equal to the ImageSize. @param[in] FWImageMaxSize is the maximum allowed size in bytes of the image. @param[in] SubsystemDeviceId is the identifier of the revision of Dimm (AEP vs BPS) @retval TRUE if the Image is valid for the update. @retval FALSE if the Image is not valid. **/ BOOLEAN ValidateRecoverySpiImage( IN NVM_FW_IMAGE_HEADER *pImage, IN UINT64 ImageSize, IN UINT64 FWImageMaxSize, IN UINT16 SubsystemDeviceId ) { BOOLEAN ReturnValue = FALSE; COMMAND_STATUS *pTmpCmdStatus = NULL; pTmpCmdStatus = AllocateZeroPool(sizeof(COMMAND_STATUS)); if (pTmpCmdStatus == NULL) { NVDIMM_ERR("Out of memory"); ReturnValue = FALSE; goto Finish; } ReturnValue = ValidateImage(pImage, ImageSize, FWImageMaxSize, pTmpCmdStatus); if (ReturnValue) { NVDIMM_ERR("This is standard firmware image. Please provide recovery image"); ReturnValue = FALSE; goto Finish; } if (SubsystemDeviceId == SPD_DEVICE_ID_10) { NVDIMM_ERR("First generation PMem modules are not supported for SPI image recovery. A 1.x release of this software is required."); goto Finish; } if (SubsystemDeviceId != SPD_DEVICE_ID_15) { NVDIMM_ERR("A PMem module is reporting an unexpected device id. SPI image recovery is not supported."); goto Finish; } if (ImageSize != FIRMWARE_SPI_IMAGE_GEN2_SIZE_B) { NVDIMM_ERR("The image has wrong size! Please try another image."); goto Finish; } if (pImage->ModuleVendor != VENDOR_ID || pImage->ModuleType != LT_MODULE_TYPE_CSS) { NVDIMM_ERR("The firmware is not compatible with the PMem module."); goto Finish; } ReturnValue = TRUE; Finish: FREE_POOL_SAFE(pTmpCmdStatus); return ReturnValue; } /** Convert FW API version parts to a string **/ VOID ConvertFwApiVersion( OUT CHAR16 Version[FW_API_VERSION_LEN], IN UINT8 Major, IN UINT8 Minor ) { CHAR16 *tmp = NULL; NVDIMM_ENTRY(); if ((Major == 0) && (Minor == 0)) { tmp = CatSPrint(NULL, FORMAT_STR, NOT_APPLICABLE_SHORT_STR); } else { tmp = CatSPrint(NULL, L"%02d.%02d", Major, Minor); } if (tmp != NULL) { StrnCpyS(Version, FW_API_VERSION_LEN, tmp, FW_API_VERSION_LEN - 1); FREE_POOL_SAFE(tmp); } NVDIMM_EXIT(); } /** Convert FW version parts to a string **/ VOID ConvertFwVersion( OUT CHAR16 Version[FW_VERSION_LEN], IN UINT8 Product, IN UINT8 Revision, IN UINT8 SecurityVersion, IN UINT16 Build ) { CHAR16 *tmp = NULL; NVDIMM_ENTRY(); if (FW_VERSION_UNDEFINED_BY_VERSION(Product, Revision, SecurityVersion, Build)) { tmp = CatSPrint(NULL, FORMAT_STR, NOT_APPLICABLE_SHORT_STR); } else { tmp = CatSPrint(NULL, L"%02d.%02d.%02d.%04d", Product, Revision, SecurityVersion, Build); } if (tmp != NULL) { StrnCpyS(Version, FW_VERSION_LEN, tmp, FW_VERSION_LEN - 1); FREE_POOL_SAFE(tmp); } NVDIMM_EXIT(); } /** Check if new FW version is staged @param[in] StagedFwVersion Info about staged firmware version @retval TRUE if new FW version is staged @retval FALSE if new FW version is not staged **/ BOOLEAN IsFwStaged( IN FIRMWARE_VERSION StagedFwVersion ) { BOOLEAN Staged = FALSE; if (StagedFwVersion.FwProduct != 0 || StagedFwVersion.FwRevision != 0 || StagedFwVersion.FwSecurityVersion != 0 || StagedFwVersion.FwBuild != 0) { Staged = TRUE; } return Staged; } /** Searches for the file in the provided working directory or the root of all connected devices compatible with the EfiSimpleFileSystemProtocol. The function stops on first file matching the name. After that the function reads the file and examine its FW Image Header. If the file header passes the checks, the function returns TRUE. Otherwise the function returns FALSE, and the detailed error can be found in the ppError OUT variable. It is the callers responsibility to free the allocated memory for the OUT variables using FreePool() function. @param[in] pFilePath - the Unicode string representing the file path relative to the devices root directory or the provided working directory. @param[in] pWorkingDirectory - the Unicode string representing the working directory relative to the devices root directory. The file path is simply appended to the working directory path. @param[in] FlashSPI flag indicates if this is standard or SPI image @param[in] SubsystemDeviceId identifier for dimm generation @param[in] FWImageMaxSize is the maximum allowed size in bytes of the image. @param[out] ppImageHeader the pointer to the pointer of the Image Header that has been read from the file. It takes NULL value if there was a reading error. @param[out] pCommandStatus structure containing detailed NVM error codes @retval TRUE if the file is valid for the update. @retval FALSE if the file is not valid. **/ BOOLEAN LoadFileAndCheckHeader( IN CHAR16 *pFilePath, IN CONST CHAR16 *pWorkingDirectory OPTIONAL, IN BOOLEAN FlashSPI, IN UINT16 SubsystemDeviceId, IN UINT64 FWImageMaxSize, OUT NVM_FW_IMAGE_HEADER **ppImageHeader, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_FILE_HANDLE FileHandle; NVM_SPI_DIRECTORY_GEN2 SpiDirectory; BOOLEAN ReturnValue = TRUE; UINT64 BuffSize = 0; UINT64 FileSize = 0; UINT64 BuffSizeTemp = 0; UINT64 BuffSpiSize = sizeof(SpiDirectory); BOOLEAN VerifyNormalImage = FALSE; NVDIMM_ENTRY(); CHECK_NULL_ARG(pFilePath, Finish); CHECK_NULL_ARG(ppImageHeader, Finish); CHECK_NULL_ARG(pCommandStatus, Finish); ZeroMem(&FileHandle, sizeof(FileHandle)); ZeroMem(&SpiDirectory, sizeof(SpiDirectory)); ReturnCode = OpenFileBinary(pFilePath, &FileHandle, pWorkingDirectory, FALSE); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("OpenFile returned: " FORMAT_EFI_STATUS ".\n", ReturnCode); CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_FILE_NOT_VALID); ReturnValue = FALSE; goto Finish; } ReturnCode = GetFileSize(FileHandle, &FileSize); BuffSize = FileSize; if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("GetFileSize returned: " FORMAT_EFI_STATUS ".\n", ReturnCode); CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_FILE_IMG_INFO_NOT_ACCESSIBLE); ReturnValue = FALSE; goto FinishClose; } if (SubsystemDeviceId == SPD_DEVICE_ID_10) { if ((!FlashSPI && BuffSize > FWImageMaxSize) || (FlashSPI && BuffSize > FIRMWARE_SPI_IMAGE_GEN1_SIZE_B)) { NVDIMM_ERR("File size is too large. It equals: %d.\n", BuffSize); CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_FILE_TOO_LARGE); ReturnValue = FALSE; goto FinishClose; } } else if (SubsystemDeviceId == SPD_DEVICE_ID_15) { if ((!FlashSPI && BuffSize > FWImageMaxSize) || (FlashSPI && BuffSize > FIRMWARE_SPI_IMAGE_GEN2_SIZE_B)) { NVDIMM_ERR("File size is too large. It equals: %d.\n", BuffSize); CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_FILE_TOO_LARGE); ReturnValue = FALSE; goto FinishClose; } } else if (SubsystemDeviceId == SPD_DEVICE_ID_20) { if ((!FlashSPI && BuffSize > FWImageMaxSize)) { NVDIMM_ERR("File size is too large. It equals: %d.\n", BuffSize); CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_FILE_TOO_LARGE); ReturnValue = FALSE; goto FinishClose; } } else { NVDIMM_ERR("Unknown Subsystem Device Id received: %d.\n", SubsystemDeviceId); CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_UNKNOWN_SUBSYSTEM_DEVICE); ReturnValue = FALSE; goto FinishClose; } if (BuffSize < sizeof(NVM_FW_IMAGE_HEADER)) { NVDIMM_ERR("File size is too small. It equals: %d.\n", BuffSize); CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_FILE_TOO_SMALL); ReturnValue = FALSE; goto FinishClose; } /** In this case it is possible that user provided valid, standard FW image instead of FlashSPI one. We have to verify and inform about such case **/ if (FlashSPI && BuffSize <= FWImageMaxSize) { VerifyNormalImage = TRUE; } /** Here we cast from UINT64 to UINTN, because the ShellReadFile takes UINTN as the buffer size. Fortunately our buffer will be smaller even if UINTN is 32-bit. **/ BuffSize = sizeof(NVM_FW_IMAGE_HEADER); *ppImageHeader = AllocatePool(BuffSize); if (*ppImageHeader == NULL) { CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_MEM_ALLOCATION_ERROR_FW_IMG); ReturnValue = FALSE; goto FinishClose; } BuffSizeTemp = BuffSize; if (FlashSPI && !VerifyNormalImage) { ReturnCode = FileHandle->Read(FileHandle, &BuffSpiSize, &SpiDirectory); if (EFI_ERROR(ReturnCode) || BuffSpiSize != sizeof(SpiDirectory)) { CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_FILE_READ_ERROR); ReturnValue = FALSE; goto FinishClose; } ReturnCode = FileHandle->SetPosition(FileHandle, SpiDirectory.FwImageStage1Offset); if (EFI_ERROR(ReturnCode)) { CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_FILE_READ_ERROR); ReturnValue = FALSE; goto FinishClose; } } ReturnCode = FileHandle->Read(FileHandle, &BuffSize, *ppImageHeader); /** If the read function returned an error OR we read less bytes that the file length equals. **/ if (EFI_ERROR(ReturnCode) || BuffSize != BuffSizeTemp) { CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_FILE_READ_ERROR); ReturnValue = FALSE; goto FinishClose; } if (FlashSPI) { ReturnValue = ValidateRecoverySpiImage(*ppImageHeader, FileSize, FWImageMaxSize, SubsystemDeviceId); } else { ReturnValue = ValidateImage(*ppImageHeader, FileSize, FWImageMaxSize, pCommandStatus); } FinishClose: FileHandle->Close(FileHandle); Finish: NVDIMM_EXIT_I64(ReturnValue); return ReturnValue; } /** Calculates the checksum of passed in buffer and keeps a running value. Used by CR FW for computing their checksum, used in our code for get/set fconfig data. @param[in] pBuffer Pointer to buffer whose checksum needs to be calculated. @param[in] NumBytes Number of bytes inside buffer to calculate checksum on. @param[in] Csum current running checksum. @retval The computed checksum **/ UINT32 RunningChecksum( IN VOID *pBuffer, IN UINT32 NumBytes, IN UINT32 Csum) { UINT32 Index; UINT8 *pU8Buffer; Csum = (~(Csum) + 1); pU8Buffer = pBuffer; for (Index = 0; Index < NumBytes; Index++) { Csum = Csum + pU8Buffer[Index] * (1 << (8 * (Index % 4))); } Csum = ~(Csum) + 1; return Csum; } ipmctl-03.00.00.0485/DcpmPkg/common/FwUtility.h000066400000000000000000000366321440615110200205530ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _FW_UTILITY_H_ #define _FW_UTILITY_H_ #include "NvmTypes.h" #include "NvmStatus.h" #define MAX_FIRMWARE_IMAGE_SIZE_KB 788 #define FIRMWARE_RECOVERY_IMAGE_SPI_GEN1_SIZE_KB 1024 #define FIRMWARE_RECOVERY_IMAGE_SPI_GEN2_SIZE_KB 2048 /** The maximum file size that a new firmware image can have - in bytes. **/ #define MAX_FIRMWARE_IMAGE_SIZE_B KIB_TO_BYTES(MAX_FIRMWARE_IMAGE_SIZE_KB) #define FIRMWARE_SPI_IMAGE_GEN1_SIZE_B KIB_TO_BYTES(FIRMWARE_RECOVERY_IMAGE_SPI_GEN1_SIZE_KB) #define FIRMWARE_SPI_IMAGE_GEN2_SIZE_B KIB_TO_BYTES(FIRMWARE_RECOVERY_IMAGE_SPI_GEN2_SIZE_KB) // Keep this updated. Used for determining max input file size for now #define MAX_FIRMWARE_SPI_IMAGE_SIZE_B FIRMWARE_SPI_IMAGE_GEN2_SIZE_B #define NO_FW_GIVEN_VERSION_MSG L"None" #define UPDATE_FIRMWARE_SMALL_PAYLOAD_DATA_PACKET_SIZE 64 /** Firmware types **/ #define FW_TYPE_PRODUCTION 29 #define FW_TYPE_DFX 30 #define FW_TYPE_DEBUG 31 #define FW_COMMIT_ID_LENGTH 40 #define FW_COMMIT_ID_STR_LENGTH 41 #define FW_BUILD_CONFIGURATION_LENGTH 16 #define FW_BUILD_CONFIGURATION_STR_LENGTH 17 /** Staged FW statuses **/ #define FW_NO_NEW_FW_STAGED 0 #define FW_NEW_FW_STAGED 1 /** Firmware update statuses **/ #define FW_UPDATE_STATUS_STAGED_SUCCESS 1 #define FW_UPDATE_STATUS_LOAD_SUCCESS 2 #define FW_UPDATE_STATUS_FAILED 3 /** Quiesce required values **/ #define QUIESCE_NOT_REQUIRED 0 #define QUIESCE_REQUIRED 1 /** Staged fw activatable values **/ #define STAGED_FW_NOT_ACTIVATABLE 0 #define STAGED_FW_ACTIVATABLE 1 #define IN_MB_SIZE (1 << 20) //!< Size of the OS mailbox large input payload #define OUT_MB_SIZE (1 << 20) //!< Size of the OS mailbox large output payload #define IN_PAYLOAD_SIZE (128) //!< Total size of the input payload registers #define OUT_PAYLOAD_SIZE (128) //!< Total size of the output payload registers #define MB_COMPLETE 0x1 #define STATUS_MASK 0xFF #define DETAILS_STR L"Details: " #define DETAILS_CANT_USE_IMAGE DETAILS_STR L"Can't use " FORMAT_STR L"on " FORMAT_STR #define DETAILS_SVNDE_NOT_ENABLED DETAILS_STR L"SVNDE is not enabled" #define DETAILS_REVISION_NUMBER_MISMATCH DETAILS_STR L"Revision number mismatch" #define DETAILS_PRODUCT_NUMBER_MISMATCH DETAILS_STR L"Product number mismatch" #define DETAILS_FILE_READ_ERROR DETAILS_STR L"Could not read the file" #define DETAILS_MEM_ALLOCATION_ERROR_FW_IMG DETAILS_STR L"Could not allocate memory for the firmware image" #define DETAILS_FILE_TOO_SMALL DETAILS_STR L"The file is too small" #define DETAILS_FILE_TOO_LARGE DETAILS_STR L"The file is too large" #define DETAILS_UNKNOWN_SUBSYSTEM_DEVICE DETAILS_STR L"Subsystem Device Id is unknown" #define DETAILS_FILE_IMG_INFO_NOT_ACCESSIBLE DETAILS_STR L"Could not get the file information" #define DETAILS_FILE_NOT_VALID DETAILS_STR L"The specified source file is not valid" #define DETAILS_WRONG_IMAGE_SIZE DETAILS_STR L"Wrong image size" #define DETAILS_IMAGE_NOT_ALIGNED DETAILS_STR L"Wrong data size - buffer not aligned" #define DETAILS_MODULE_TYPE_NOT_COMPATIBLE DETAILS_STR L"Module type not compatible" #define DETAILS_VENDOR_NOT_COMPATIBLE DETAILS_STR L"Vendor not compatible" #pragma pack(push) #pragma pack(1) typedef struct { UINT8 Opcode; UINT8 SubOpcode; UINT8 TransportInterface; UINT8 Reserved[5]; UINT32 Timeout; UINT8 Data[IN_PAYLOAD_SIZE]; } NVM_INPUT_PAYLOAD_SMBUS_OS_PASSTHRU; #pragma pack(pop) // Additional bytes to deal with DSM calls #define IN_PAYLOAD_SIZE_EXT_PAD (sizeof(NVM_INPUT_PAYLOAD_SMBUS_OS_PASSTHRU) - IN_PAYLOAD_SIZE) #pragma pack(push) #pragma pack(1) typedef struct { UINT32 InputPayloadSize; UINT32 LargeInputPayloadSize; UINT32 OutputPayloadSize; UINT32 LargeOutputPayloadSize; // Additional buffer for potential OS special passthrough // See use of SubopExtVendorSpecific in PassThru() UINT8 InputPayload[IN_PAYLOAD_SIZE + IN_PAYLOAD_SIZE_EXT_PAD]; UINT8 LargeInputPayload[IN_MB_SIZE]; UINT8 OutPayload[OUT_PAYLOAD_SIZE]; UINT8 LargeOutputPayload[OUT_MB_SIZE]; UINT32 DimmID; UINT8 Opcode; UINT8 SubOpcode; UINT8 Status; #ifdef OS_BUILD UINT8 DsmStatus; #endif } NVM_FW_CMD; #pragma pack(pop) // FW commands that are supposed to work even if the module firmware is unresponsive #define FW_CMD_INTERFACE_INDEPENDENT(Opcode, SubOpcode) (Opcode == PtEmulatedBiosCommands && SubOpcode == SubopGetBSR) /** Version struct definition **/ typedef union { struct { UINT8 Digit2:4; UINT8 Digit1:4; } Nibble; UINT8 Version; } NVM_VERSION_BYTE; typedef union { struct { UINT16 Digit4:4; UINT16 Digit3:4; UINT16 Digit2:4; UINT16 Digit1:4; } Nibble; UINT16 Build; } NVM_BUILD_WORD; typedef union { struct { UINT8 Digit2; UINT8 Digit1; } Byte; UINT16 Version; } NVM_API_VERSION; typedef union { struct { UINT16 Year:16; UINT8 Month:8; UINT8 Day:8; } Separated; UINT32 Value; } NVM_DATE; /** All BCD encoded aa.bb.cc.dddd **/ #pragma pack(push) #pragma pack(1) typedef struct _NVM_FW_VERSION { NVM_BUILD_WORD BuildNumber; //!< dddd NVM_VERSION_BYTE SecurityRevisionNumber; //!< cc NVM_VERSION_BYTE RevisionNumber; //!< bb NVM_VERSION_BYTE ProductNumber; //!< aa } NVM_FW_VERSION; /** FW Image header: Intel CSS Header (128 bytes) **/ typedef struct { UINT32 ModuleType; //!< moduleType = LT_MODULE_TYPE_CSS UINT32 HeaderLen; //!< headerLen == dword_sizeof(fixedHeader) + (modulusSize * 2) + exponentSize UINT32 HeaderVersion; //!< bits [31:16] are major version, bits [15:0] are minor version UINT32 ModuleID; //!< if bit 31 == 1 this is a debug module UINT32 ModuleVendor; //!< moduleVendor = 0x00008086 NVM_DATE Date; //!< BCD format: yyyymmdd UINT32 Size; //!< Size of entire module (header, crypto(modulus, exponent, signature), data) in DWORDs UINT32 KeySize; //!< Size of RSA public key in DWORDs UINT32 ModulusSize; //!< Size of RSA public key modulus in DWORDs UINT32 ExponentSize; //!< Size of RSA public key exponent UINT8 ImageType; //!< Image type: 0x1D - PRQ, 0x1E - DFX, 0x1F - DBG NVM_FW_VERSION ImageVersion; //!< Image version UINT32 PerPartIdHigh; //!< Part id high UINT32 PerPartIdLow; //!< Part id low UINT32 ImageSize; //!< Size of (header , signature , data) in DWORDs (ie size - 65 DWORDs) NVM_API_VERSION FwApiVersion; //!< BCD format: aa.bb UINT8 StageNumber; //!< Stage number: 0 - stage1 , 1 - stage 2 UINT32 fwImageStartAddr; //!< Firmware SRAM start address. This value must agree with actual image start produced by the .ld file. The fwImageStartAddr address MUST be 64 byte aligned. UINT16 VendorId; //!< Specifies vendor. Intel (0x8086) UINT16 DeviceId; //!< Specifies root arch type (ekv, bwv, cwv, etc .). UINT16 RevisionId; //!< Specifies base and metal steppings of arch (A0, A1 , S0, S1, B0, B2, etc.). UINT8 NumberOfStages; //!< Specifies the number of expected stages to load. 1 - one stage to load , 2 - two stages to load UINT8 Reserved[56]; } NVM_FW_IMAGE_HEADER; /** SPI Directory structure that holds SPI memory map **/ // Copied March 2018 from spi_memory_map_ekvs1.h #define SPI_DIRECTORY_VERSION_GEN1 1 // Copied August 2019 from spi_memory_map_bwv.h #define SPI_DIRECTORY_VERSION_GEN2 2 /** Tests a FW version to see if it is an undefined version @param FwProduct @param FwRevision @param FwSecurityVersion @param FwBuild @return TRUE if relevant fields are all 0. **/ #define FW_VERSION_UNDEFINED_BY_VERSION(FwProduct, FwRevision, FwSecurityVersion, FwBuild) (FwProduct == 0 && \ FwRevision == 0 && \ FwSecurityVersion == 0 && \ FwBuild == 0) /** Tests a FW version to see if it is an undefined version @param FirmwareVersionStruct a FIRMWARE_VERSION struct @return TRUE if relevant fields are all 0. **/ #define FW_VERSION_UNDEFINED(FirmwareVersionStruct) FW_VERSION_UNDEFINED_BY_VERSION(\ FirmwareVersionStruct.FwProduct, \ FirmwareVersionStruct.FwRevision, \ FirmwareVersionStruct.FwSecurityVersion, \ FirmwareVersionStruct.FwBuild) typedef struct { UINT16 DirectoryVersion; UINT16 DirectorySize; UINT32 SoftFusesDataOffset; UINT32 DirectoryCopyOffset; // Not used UINT32 MfgCypherOffset; UINT32 FwImageOffset; UINT32 FwImageDfxOffset; UINT32 SpdDataOffset; UINT32 MigrationDataOffset; UINT32 FwImageCopyOffset; UINT32 SxpSavedRegistersOffset; UINT32 Reserved_1; // was DdrtSavedRegistersOffset; UINT32 BurninInputDataOffset; UINT32 BurninOutputDataOffset; UINT32 SxpRankInterleavingOffset; UINT32 DdrtIoMmrcTableOffset; UINT32 SxpIoMmrcTableOffset; UINT32 Reserved_2; // was FwStateDataOffset; UINT32 FconfigDataOffset; UINT32 SxpTimingParametersOffset; UINT32 SxpTrainingReportOffset; UINT32 SxpRmtResultsOffset; UINT32 SxpRawTrainingDataOffset; UINT32 PreInjectionModuleFrameworkOffset; UINT32 reserved[8]; UINT8 reservedu8[3]; UINT8 SpiEndOfDirectory; } NVM_SPI_DIRECTORY_GEN1; typedef struct { UINT16 DirectoryVersion; UINT16 DirectorySize; UINT32 SoftFusesDataOffset; UINT32 FwImageStage1Offset; UINT32 FwImageStage2Offset; UINT32 FwImageCopyStage1Offset; UINT32 FwImageCopyStage2Offset; UINT32 FwImageDfxStage1Offset; UINT32 FwImageDfxStage2Offset; UINT32 MigrationDataOffset; UINT32 SxpSavedRegistersOffset; UINT32 SxpTrainingReportOffset; UINT32 SxpRmtResultsOffset; UINT32 SxpRawTrainingDataOffset; UINT32 BurninInputDataOffset; UINT32 BurninOutputDataOffset; UINT32 FconfigDataOffset; UINT32 SxpTimingParametersOffset; UINT32 PreInjectionModuleFrameworkOffset; UINT32 SpiDebugDataOffset; UINT32 NlogBackupOffset; } NVM_SPI_DIRECTORY_GEN2; #pragma pack(pop) /** FW Image header information **/ typedef struct { NVM_FW_VERSION ImageVersion; UINT8 FirmwareType; UINT32 ModuleVendor; //!< moduleVendor = 0x00008086 NVM_DATE Date; //!< BCD format: yyyymmdd UINT32 Size; //!< Size of entire module (header, crypto, data) in DWORDs } NVM_FW_IMAGE_INFO; /** The persistent memory module type code (taken from FW image) **/ #define LT_MODULE_TYPE_CSS 0x6 /** Checks if the firmware image is valid for an update. Please note that even if this function returns TRUE, the image may not be fully valid. There are additional security and CRC checks made by the DIMM that may fail. @param[in] pImage is the buffer that contains the image we want to validate. @param[in] ImageSize is the size in bytes of the valid image data in the buffer. The buffer must be bigger or equal to the ImageSize. @param[in] FWImageMaxSize is the maximum allowed size in bytes of the image. @param[out] ppError is the pointer to a Unicode string that will contain the details about the failure. The caller is responsible to free the allocated memory with the FreePool function. @retval TRUE if the Image is valid for the update. @retval FALSE if the Image is not valid. **/ BOOLEAN ValidateImage( IN NVM_FW_IMAGE_HEADER *pImage, IN UINT64 ImageSize, IN UINT64 FWImageMaxSize, OUT COMMAND_STATUS *pCommandStatus ); /** Checks if the firmware image is valid for an recovery. Please note that even if this function returns TRUE, the image may not be fully valid. There are additional security and CRC checks made by the DIMM that may fail. @param[in] pImage is the buffer that contains the image we want to validate. @param[in] ImageSize is the size in bytes of the valid image data in the buffer. The buffer must be bigger or equal to the ImageSize. @param[in] FWImageMaxSize is the maximum allowed size in bytes of the image. @param[in] SubsystemDeviceId is the identifier of the Dimm revision @retval TRUE if the Image is valid for the update. @retval FALSE if the Image is not valid. **/ BOOLEAN ValidateRecoverySpiImage( IN NVM_FW_IMAGE_HEADER *pImage, IN UINT64 ImageSize, IN UINT64 FWImageMaxSize, IN UINT16 SubsystemDeviceId ); /** Searches for the file in the provided working directory or the root of all connected devices compatible with the EfiSimpleFileSystemProtocol. The function stops on first file matching the name. After that the function reads the file and examine its FW Image Header. If the file header passes the checks, the function returns TRUE. Otherwise the function returns FALSE, and the detailed error can be found in the ppError OUT variable. It is the callers responsibility to free the allocated memory for the OUT variables using FreePool() function. @param[in] pFilePath - the Unicode string representing the file path relative to the devices root directory or the provided working directory. @param[in] pWorkingDirectory - the Unicode string representing the working directory relative to the devices root directory. The file path is simply appended to the working directory path. @param[in] Recovery flag indicates if this is standard or recovery image @param[in] SubsystemDeviceId identifier for dimm generation @param[in] FWImageMaxSize is the maximum allowed size in bytes of the image. @param[out] ppImageHeader the pointer to the pointer of the Image Header that has been read from the file. It takes NULL value if there was a reading error. @param[out] pCommandStatus structure containing detailed NVM error codes @retval TRUE if the file is valid for the update. @retval FALSE if the file is not valid. **/ BOOLEAN LoadFileAndCheckHeader( IN CHAR16 *pFilePath, IN CONST CHAR16 *pWorkingDirectory OPTIONAL, IN BOOLEAN Recovery, IN UINT16 SubsystemDeviceId, IN UINT64 FWImageMaxSize, OUT NVM_FW_IMAGE_HEADER **ppImageHeader, OUT COMMAND_STATUS *pCommandStatus ); /** Convert FW version parts to a string **/ VOID ConvertFwVersion( OUT CHAR16 Version[FW_VERSION_LEN], IN UINT8 Product, IN UINT8 Revision, IN UINT8 SecurityVersion, IN UINT16 Build ); /** Convert FW API version parts to a string **/ VOID ConvertFwApiVersion( OUT CHAR16 Version[FW_API_VERSION_LEN], IN UINT8 Major, IN UINT8 Minor ); /** Check if new FW version is staged @param[in] StagedFwVersion Info about staged firmware version @retval TRUE if new FW version is staged @retval FALSE if new FW version is not staged **/ BOOLEAN IsFwStaged( IN FIRMWARE_VERSION StagedFwVersion ); /** Calculates the checksum of passed in buffer and keeps a running value. Used by CR FW for computing their checksum, used in our code for get/set fconfig data. @param[in] pBuffer Pointer to buffer; whose checksum needs to be calculated. @param[in] NumBytes Number of bytes inside buffer to calculate checksum on. @param[in] Csum current running checksum. @retval The computed checksum **/ UINT32 RunningChecksum( IN VOID *pBuffer, IN UINT32 NumBytes, IN UINT32 Csum ); #endif /** _FW_UTILITY_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/FwVersion.h000066400000000000000000000007041440615110200205240ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _FWVERSION_H_ #define _FWVERSION_H_ /** Minimum supported version of FW API: 1.2 **/ #define MIN_FIS_SUPPORTED_BY_THIS_SW_MAJOR 1 #define MIN_FIS_SUPPORTED_BY_THIS_SW_MINOR 2 /** Maximum supported version of FW API: 3.5 **/ #define MAX_FIS_SUPPORTED_BY_THIS_SW_MAJOR 3 #define MAX_FIS_SUPPORTED_BY_THIS_SW_MINOR 5 #endif /** _FWVERSION_H_ **/ipmctl-03.00.00.0485/DcpmPkg/common/LbaCommon.c000066400000000000000000000101041440615110200204370ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include "LbaCommon.h" /** Get current Namespace Index Id. Only one of two Namespace Indexes is valid at time. This function checks sequence numbers of both Indexes and returns current Index Id. @param[in] pLabelStorageArea Pointer to a LSA structure @param[out] pCurrentIndex Current index position in LSA structure @param[out] pNextIndex Next index position to be updated in LSA structure @retval EFI_SUCCESS Current Index position found @retval EFI_INVALID_PARAMETER NULL pointer parameter provided **/ EFI_STATUS GetLsaIndexes( IN LABEL_STORAGE_AREA *pLsa, OUT UINT16 *pCurrentIndex, OUT UINT16 *pNextIndex ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 FirstSeq = 0; UINT32 SecondSeq = 0; UINT32 SeqSum = 0; UINT32 CurrentSeq = 0; UINT32 OtherSeq = 0; NVDIMM_ENTRY(); if (pLsa == NULL || (pCurrentIndex == NULL && pNextIndex == NULL)) { goto Finish; } FirstSeq = pLsa->Index[FIRST_INDEX_BLOCK].Sequence; SecondSeq = pLsa->Index[SECOND_INDEX_BLOCK].Sequence; SeqSum = FirstSeq + SecondSeq; if (SeqSum == 0 || SeqSum > 6) { //invalid sequence numbers NVDIMM_DBG("Invalid sequence numbers FirstSeq: %d, SecondSeq: %d", FirstSeq, SecondSeq); goto Finish; } if (SeqSum == 4) { //seq num case [3,1] [1,3] lower one is newer CurrentSeq = MIN(FirstSeq, SecondSeq); OtherSeq = MAX(FirstSeq, SecondSeq); } else { //seq num all other cases, higher one is newer/valid CurrentSeq = MAX(FirstSeq, SecondSeq); OtherSeq = MIN(FirstSeq, SecondSeq); } //if both sequences are the same, use the one with higher offset: SecondSeq if (CurrentSeq == SecondSeq) { if (pCurrentIndex != NULL) { *pCurrentIndex = SECOND_INDEX_BLOCK; } if (pNextIndex != NULL) { *pNextIndex = FIRST_INDEX_BLOCK; } } else { if (pCurrentIndex != NULL) { *pCurrentIndex = FIRST_INDEX_BLOCK; } if (pNextIndex != NULL) { *pNextIndex = SECOND_INDEX_BLOCK; } } NVDIMM_DBG("[current: pos=%d seq=%d] other: pos=%d, seq=%d", pCurrentIndex != NULL ? *pCurrentIndex : -1, CurrentSeq, pNextIndex != NULL ? *pNextIndex : -1, OtherSeq); ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Function checks Namespace slot status. @param[in] pIndex Index Block in which to update free status @param[in] SlotNumber Number of a slot on which to update status @param[out pSlotStatus Return value representing current status. This can be SLOT_FREE or SLOT_USED. @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_SUCCESS Operation successful **/ EFI_STATUS CheckSlotStatus( IN NAMESPACE_INDEX *pIndex, IN UINT16 SlotNumber, OUT UINT16 *pSlotStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CONST UINT16 BitsInBlock = sizeof(UINT8) * 8; // how many bits in a block CONST UINT16 BlockNumber = SlotNumber / BitsInBlock; // subsequent block number in the bitmap CONST UINT8 BitNumber = (CONST UINT8)(SlotNumber % BitsInBlock); // subsequent bit number in a block UINT8 BitValue = 0; if (pIndex == NULL || pSlotStatus == NULL) { goto Finish; } BitValue = pIndex->pFree[BlockNumber] & (1 << BitNumber); if (BitValue == 0) { *pSlotStatus = SLOT_USED; } else { *pSlotStatus = SLOT_FREE; } ReturnCode = EFI_SUCCESS; Finish: return ReturnCode; } VOID FreeLsaSafe( IN LABEL_STORAGE_AREA **ppLabelStorageArea ) { UINT32 IndexIdx = 0; NAMESPACE_INDEX *pNamespaceIndex = NULL; if (ppLabelStorageArea != NULL && *ppLabelStorageArea != NULL) { for (IndexIdx = 0; IndexIdx < NAMESPACE_INDEXES; IndexIdx++) { pNamespaceIndex = &(((*ppLabelStorageArea)->Index)[IndexIdx]); FREE_POOL_SAFE(pNamespaceIndex->pFree); FREE_POOL_SAFE(pNamespaceIndex->pReserved); } FREE_POOL_SAFE((*ppLabelStorageArea)->pLabels); FREE_POOL_SAFE(*ppLabelStorageArea); } } ipmctl-03.00.00.0485/DcpmPkg/common/LbaCommon.h000066400000000000000000000133351440615110200204550ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _LBA_COMMON_H_ #define _LBA_COMMON_H_ /** Namespace Index structure defines **/ #define NSINDEX_ALIGN 256 #define NSINDEX_FREE_ALIGN 8 #define NSINDEX_SIG_LEN 16 #define NSINDEX_MAJOR 1 #define NSINDEX_MINOR_1 1 #define NSINDEX_MINOR_2 2 #define INDEX_LABEL_SIZE_TO_BYTE(a) (128 << (a)) #define BYTE_TO_INDEX_LABEL_SIZE(a) ((a) >> 8) #define LABELS_TO_FREE_BYTES(a) ((a) >> 3) #define FREE_BLOCKS_TO_LABELS(a) ((a) << 3) #define FREE_BLOCKS_MASK_ALL_SET 0xFF #define FIRST_INDEX_BLOCK 0 #define SECOND_INDEX_BLOCK 1 #define ALL_INDEX_BLOCKS 2 #define SLOT_UNKNOWN 0 #define SLOT_FREE 1 #define SLOT_USED 2 #pragma pack(push) #pragma pack(1) typedef union { struct { UINT32 ReadOnly : 1; //!< Read-only label UINT32 Local : 1; //!< DIMM-local namespace UINT32 Reserved : 1; //!< Reserved UINT32 Updating : 1; //!< Label being updated UINT32 Reserved2 : 28; //!< Reserved } Values; UINT32 AsUint32; } LABEL_FLAGS; /** Namespace Index structure definition **/ typedef struct { CHAR8 Signature[NSINDEX_SIG_LEN]; //!< Must be "NAMESPACE_INDEX\0" UINT8 Flags[3]; //!< see flag bits below UINT8 LabelSize; //!< Size of each label in bytes in 128B UINT32 Sequence; //!< Sequence number for this index UINT64 MyOffset; //!< Offset of this index in label area UINT64 MySize; //!< Size of this index struct UINT64 OtherOffset; //!< Offset of other index UINT64 LabelOffset; //!< Offset of first label slot UINT32 NumberOfLabels; //!< Total number of label slots UINT16 Major; //!< Label area major version UINT16 Minor; //!< Label area minor version UINT64 Checksum; //!< Fletcher64 of all fields /** The size of Free[] is rounded up so the total struct size is a multiple of NSINDEX_ALIGN bytes. Any bits this allocates beyond nlabel bits must be zero. **/ UINT8 *pFree; //!< NumbeOfLabels bits to map free slots UINT8 *pReserved; //!< Padding } NAMESPACE_INDEX; /** Namespace Label structure defines **/ #define NSLABEL_NAME_LEN 63 /** Namespace Label structure definition **/ typedef struct { GUID Uuid; //!< UUID per RFC 4122 CHAR8 Name[NLABEL_NAME_LEN_WITH_TERMINATOR]; //!< Optional name (NULL-terminated) LABEL_FLAGS Flags; UINT16 NumberOfLabels; //!< Number of labels to describe this namespace UINT16 Position; //!< Labels position in set UINT64 InterleaveSetCookie; //!< Interleave set cookie UINT64 LbaSize; //!< LBA size in bytes or 0 for PMEM UINT64 Dpa; //!< DPA of NVM range on this DIMM UINT64 RawSize; //!< Size of namespace UINT32 Slot; //!< Slot of this label in label area UINT32 Unused; //!< Must be zero } NAMESPACE_LABEL_1_1; typedef struct { GUID Uuid; //!< UUID per RFC 4122 CHAR8 Name[NLABEL_NAME_LEN_WITH_TERMINATOR]; //!< Optional name (NULL-terminated) LABEL_FLAGS Flags; UINT16 NumberOfLabels; //!< Number of labels to describe this namespace UINT16 Position; //!< Labels position in set UINT64 InterleaveSetCookie; //!< Interleave set cookie UINT64 LbaSize; //!< LBA size in bytes or 0 for PMEM UINT64 Dpa; //!< DPA of NVM range on this DIMM UINT64 RawSize; //!< Size of namespace UINT32 Slot; //!< Slot of this label in label area UINT8 Alignment; //!< Advertise the preferred alignment of the data UINT8 Reserved[3]; //!< Zero GUID TypeGuid; //!< Describe the access mechanism for the DPA range GUID AddressAbstractionGuid; //!< Identifies the address abstraction mechanism for this namespace UINT8 Reserved1[88]; //!< Zero UINT64 Checksum; //!< Fletcher64 } NAMESPACE_LABEL; // 256B typedef struct { NAMESPACE_INDEX Index[NAMESPACE_INDEXES]; NAMESPACE_LABEL *pLabels; } LABEL_STORAGE_AREA; #pragma pack(pop) /** Get current Namespace Index Id. Only one of two Namespace Indexes is valid at time. This function checks sequence numbers of both Indexes and returns current Index Id. @param[in] pLabelStorageArea Pointer to a LSA structure @param[out] pCurrentIndex Current index position in LSA structure @retval EFI_SUCCESS Current Index position found @retval EFI_INVALID_PARAMETER NULL pointer parameter provided **/ EFI_STATUS GetLsaIndexes( IN LABEL_STORAGE_AREA *pLsa, OUT UINT16 *pCurrentIndex, OUT UINT16 *pNextIndex ); /** Function checks Namespace slot status. @param[in] pIndex Index Block in which to update free status @param[in] SlotNumber Number of a slot on which to update status @param[out pSlotStatus Return value representing current status. This can be SLOT_FREE or SLOT_USED. @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_SUCCESS Operation successful **/ EFI_STATUS CheckSlotStatus( IN NAMESPACE_INDEX *pIndex, IN UINT16 SlotNumber, OUT UINT16 *pSlotStatus ); VOID FreeLsaSafe( IN LABEL_STORAGE_AREA **ppLabelStorageArea ); #endif ipmctl-03.00.00.0485/DcpmPkg/common/Nlog.c000066400000000000000000000511621440615110200175000ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "Nlog.h" VOID decode_nlog_binary( struct Command *pCmd, CHAR16* decoded_file_name, UINT8* nlogbytes, UINT64 size, UINT32 dict_version, nlog_dict_entry* dict_head ) { EFI_STATUS status; BOOLEAN inv2section = FALSE; UINT32 expectedMagicNum = 11928997; UINT64 x = 0; UINT64 y = 0; UINT64 z = 0; nlog_dict_entry* entry = NULL; nlog_record* record = NULL; nlog_record* head = NULL; nlog_record* tail = NULL; UINT64 total_formatted_string_size = 0; CHAR8* total_formatted_string = NULL; UINT64 elements = 0; CHAR8** append_strs = NULL; UINT32** old_args = NULL; UINT64 old_arg_count = 0; nlog_version_v1 v1; nlog_version_v2 v2; CHAR16* kernel_str = NULL; CHAR8* ascii_kernel_str = NULL; CHAR8* system_time_set_log = "System Time Set"; UINTN ascii_kernel_str_size = 0; UINT32 system_time_set = 0; CHAR8* old_format_str = NULL; CHAR8* decode_header = NULL; UINT64 header_length = 0; UINT32 value; UINT64 node_count = 0; CHAR8* formatted_string_head = NULL; UINT64 bytes_needed = 0; UINT64 appended_count = 0; BOOLEAN dictionary_entry_was_allocated_here = FALSE; BOOLEAN hash_not_found = FALSE; nlog_record* next; PRINT_CONTEXT *pPrinterCtx = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; if (pCmd != NULL) { pPrinterCtx = pCmd->pPrintCtx; } node_count = 0; for (x = 0; x < size; x += 4) { record = NULL; entry = NULL; dictionary_entry_was_allocated_here = FALSE; hash_not_found = FALSE; value = bytes_to_u32(&nlogbytes[x]); if (x % 256 == 0) { inv2section = FALSE; v2.rawData = value; if (v2.data.magic_number == expectedMagicNum && v2.data.version == dict_version) { inv2section = TRUE; continue; } } if (0 == value) { continue; //this is not a valid record } /* check for V2 dictionary entry if this is a V2 section. If there isn't one, create a fake one for logging purposes If this is a V1 section and the value is valid for a V1 section, create a fake entry for logging purposes */ if (inv2section) { entry = get_nlog_entry(value, dict_head); if (entry == NULL) { hash_not_found = TRUE; entry = AllocateZeroPool(sizeof(nlog_dict_entry)); if (NULL == entry) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } dictionary_entry_was_allocated_here = TRUE; entry->Hash = value; entry->Args = 1; entry->LogLevel = string_copy("-"); entry->FileName = string_copy("-"); entry->LogString = string_copy("Hash %d not found in dictionary"); entry->next = NULL; entry->prev = NULL; record = AllocateZeroPool(sizeof(nlog_record)); if (NULL == record) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } record->DictEntry = entry; record->KernelTime = 0; record->ArgValues = AllocateZeroPool(record->DictEntry->Args * sizeof(UINT32*)); if (NULL == record->ArgValues) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } record->ArgValues[0] = AllocateZeroPool(sizeof(UINT32)); if (NULL == record->ArgValues[0]) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } *record->ArgValues[0] = entry->Hash; record->KernelTime = 0; record->FormattedString = NULL; record->prev = NULL; record->next = NULL; } } else { v1.rawData = value; if (0 == v1.data.args && 0 == v1.data.line_number && 0 == v1.data.module_id) { continue; } entry = AllocateZeroPool(sizeof(nlog_dict_entry)); if (NULL == entry) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } dictionary_entry_was_allocated_here = TRUE; entry->Args = v1.data.args; entry->Hash = 0; entry->LogLevel = string_copy("-"); entry->FileName = string_copy("-"); entry->LogString = NULL; entry->next = NULL; entry->prev = NULL; } //Move the pointer index to the next U32 and allocate space for a record if (record == NULL) { record = AllocateZeroPool(sizeof(nlog_record)); if (NULL == record) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } record->id = node_count; record->ArgValues = NULL; record->FormattedString = NULL; record->prev = NULL; record->next = NULL; record->KernelTime = 0; } if (record->DictEntry == NULL && entry != NULL) { record->DictEntry = entry; } if (head == NULL) { head = record; tail = record; } else { tail->next = record; record->prev = tail; tail = record; } //get the timestamp if (FALSE == hash_not_found) { x += 4; if (x >= size) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Unexpected end of buffer. 1\n"); goto Finish; } record->KernelTime = bytes_to_u32(&nlogbytes[x]); /* Gather the argument U32s according to the discovered count */ if (record->DictEntry->Args > 0) { record->ArgValues = AllocateZeroPool(record->DictEntry->Args * sizeof(UINT32*)); if (NULL == record->ArgValues) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } for (y = 0; y < record->DictEntry->Args && x < size; y++) { x += 4; if (x >= size) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Unexpected end of buffer. 3\n"); goto Finish; } record->ArgValues[y] = AllocateZeroPool(sizeof(UINT32)); if (NULL == record->ArgValues[y]) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } *record->ArgValues[y] = bytes_to_u32(&nlogbytes[x]); } } if (FALSE == inv2section) { elements = 3 + record->DictEntry->Args; append_strs = AllocateZeroPool(sizeof(CHAR8*) * elements); if (NULL == append_strs) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } append_strs[0] = string_copy("V1 Log module: 0x%X, "); append_strs[1] = string_copy("line: %d, "); append_strs[2] = string_copy("args: "); for (z = 3; z < elements; z++) { append_strs[z] = string_copy("0x%X "); } old_args = record->ArgValues; old_arg_count = record->DictEntry->Args; record->DictEntry->Args = old_arg_count + 2; record->ArgValues = AllocateZeroPool(record->DictEntry->Args * sizeof(UINT32*)); if (NULL == record->ArgValues) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } record->ArgValues[0] = AllocateZeroPool(sizeof(UINT32)); if (NULL == record->ArgValues[0]) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } record->ArgValues[1] = AllocateZeroPool(sizeof(UINT32)); if (NULL == record->ArgValues[1]) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } *record->ArgValues[0] = v1.data.module_id; *record->ArgValues[1] = v1.data.line_number; for (z = 0; z < old_arg_count; z++) { record->ArgValues[z + 2] = AllocateZeroPool(sizeof(UINT32)); *record->ArgValues[z + 2] = *old_args[z]; } for (z = 0; z < old_arg_count; z++) { FREE_POOL_SAFE(old_args[z]); } FREE_POOL_SAFE(old_args); record->DictEntry->LogString = string_array_concat(append_strs, elements, TRUE, &z); } } record->FormattedString = nlog_format(record->DictEntry->LogString, record->ArgValues, record->DictEntry->Args); if (TRUE == hash_not_found) { elements = 2; old_format_str = record->FormattedString; append_strs = AllocateZeroPool(sizeof(CHAR8*) * elements); if (NULL == append_strs) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } append_strs[0] = old_format_str; append_strs[1] = string_copy("\n"); record->FormattedString = string_array_concat(append_strs, elements, FALSE, &record->FormattedStringLen); } else { elements = 8; old_format_str = record->FormattedString; append_strs = AllocateZeroPool(sizeof(CHAR8*) * elements); if (NULL == append_strs) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } // Look for log eg: \"System Time Set at boot. Time: 0x0_55bbb4a6\". Convert to time format string only for real kernel time and not system ticks. if (((system_time_set != 0) && (record->KernelTime >= system_time_set)) || (AsciiStrnCmp(record->FormattedString + 1, system_time_set_log, string_length(system_time_set_log)) == 0)) { system_time_set = record->KernelTime; kernel_str = GetTimeFormatString((UINT64)record->KernelTime, TRUE); if (NULL == kernel_str) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to convert the timestamp into readable string format\n"); goto Finish; } ascii_kernel_str_size = StrLen(kernel_str) + 1; ascii_kernel_str = AllocateZeroPool(sizeof(CHAR8) * ascii_kernel_str_size); UnicodeStrToAsciiStrS(kernel_str, ascii_kernel_str, ascii_kernel_str_size); append_strs[0] = pad_left(ascii_kernel_str, 28, ' ', TRUE); } else { ascii_kernel_str = u32_to_a(record->KernelTime, FALSE, 0, FALSE); append_strs[0] = pad_left(ascii_kernel_str, 28, ' ', TRUE); } append_strs[1] = string_copy(" :: "); append_strs[2] = pad_left(record->DictEntry->FileName, 27, ' ', FALSE); append_strs[3] = string_copy(" :: "); append_strs[4] = pad_left(record->DictEntry->LogLevel, 7, ' ', FALSE); append_strs[5] = string_copy(" :: "); append_strs[6] = record->FormattedString; append_strs[7] = string_copy("\n"); record->FormattedString = string_array_concat(append_strs, elements, FALSE, &record->FormattedStringLen); } total_formatted_string_size += record->FormattedStringLen; FREE_POOL_SAFE(kernel_str); if (append_strs) { for (z = 0; z < elements; z++) { FREE_POOL_SAFE(append_strs[z]); } FREE_POOL_SAFE(append_strs); } if (record->ArgValues) { for (z = 0; z < record->DictEntry->Args; z++) { FREE_POOL_SAFE(record->ArgValues[z]); } FREE_POOL_SAFE(record->ArgValues); record->ArgValues = NULL; } if (entry != NULL && dictionary_entry_was_allocated_here) { FREE_POOL_SAFE(entry->FileName); FREE_POOL_SAFE(entry->LogLevel); FREE_POOL_SAFE(entry->LogString); FREE_POOL_SAFE(entry); entry = NULL; record->DictEntry = NULL; } node_count++; } /* build the output string and dump it to the file */ decode_header = string_copy("TIMESTAMP :: FILE :: LEVEL :: LOG\n=====================================================================================\n"); header_length = string_length(decode_header); total_formatted_string_size += header_length; bytes_needed = (sizeof(CHAR8*) * total_formatted_string_size) + sizeof(CHAR8*); total_formatted_string = AllocateZeroPool(bytes_needed); if (NULL == total_formatted_string) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate %lu bytes to dump the decoded output\n", bytes_needed); goto Finish; } formatted_string_head = total_formatted_string; appended_count = 0; if (header_length > 0) { MyMemCopy(formatted_string_head, header_length, decode_header); formatted_string_head = formatted_string_head + header_length; } FREE_POOL_SAFE(decode_header); record = head; while (record) { if (record->FormattedStringLen > 0) { MyMemCopy(formatted_string_head, record->FormattedStringLen, record->FormattedString); formatted_string_head = formatted_string_head + record->FormattedStringLen; } FREE_POOL_SAFE(record->FormattedString); record->FormattedString = NULL; appended_count++; record = record->next; } status = DumpToFile(decoded_file_name, total_formatted_string_size, total_formatted_string, TRUE); if (EFI_ERROR(status)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to write record to file %lu\n", status); goto Finish; } PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Decoded %lu records to file " FORMAT_STR "\n", node_count, decoded_file_name); Finish: if (NULL != old_args) { for (z = 0; z < old_arg_count; z++) { FREE_POOL_SAFE(old_args[z]); } FREE_POOL_SAFE(old_args); } if (NULL != total_formatted_string) { FREE_POOL_SAFE(total_formatted_string); } if (NULL != append_strs) { for (z = 0; z < elements; z++) { FREE_POOL_SAFE(append_strs[z]); } FREE_POOL_SAFE(append_strs); } while (head != NULL) { next = head->next; if (head->ArgValues) { for (z = 0; z < head->DictEntry->Args; z++) { FREE_POOL_SAFE(head->ArgValues[z]); } FREE_POOL_SAFE(head->ArgValues); } if (head->FormattedString) { FREE_POOL_SAFE(head->FormattedString); } FREE_POOL_SAFE(head); head = next; } if (entry != NULL && dictionary_entry_was_allocated_here) { FREE_POOL_SAFE(entry->FileName); FREE_POOL_SAFE(entry->LogLevel); FREE_POOL_SAFE(entry->LogString); FREE_POOL_SAFE(entry); } } nlog_dict_entry* get_nlog_entry( UINT32 hashVal, nlog_dict_entry* head ) { nlog_dict_entry* ret = head; while (NULL != ret) { if (ret->Hash == hashVal) { return ret; } ret = ret->next; } return NULL; } /* Loads test binary dumps for the purpose of decoding them */ VOID ** LoadBinaryFile( CHAR16 * pLoadUserPath, OUT UINT64 *bytes_read) { EFI_STATUS status; EFI_DEVICE_PATH_PROTOCOL *pDevicePathProtocol = NULL; VOID *buffer = NULL; CHAR16 * pDictPath = AllocateZeroPool(OPTION_VALUE_LEN * sizeof(*pDictPath)); if (pDictPath == NULL) { Print(L"Failed to allocate memory for the path\n"); goto Finish; } status = GetDeviceAndFilePath(pLoadUserPath, pDictPath, &pDevicePathProtocol); if (EFI_ERROR(status)) { Print(L"GetDeviceAndFilePath Failed\n"); goto Finish; } status = FileRead(pDictPath, pDevicePathProtocol, 0x1FFFFFFF, TRUE, bytes_read, (VOID **)&buffer); if (EFI_ERROR(status) || NULL == buffer) { Print(L"FileRead Failed\n"); *bytes_read = 0; goto Finish; } Finish: if (pDictPath != NULL) { FREE_POOL_SAFE(pDictPath); } return buffer; } nlog_dict_entry* load_nlog_dict( struct Command *pCmd, CHAR16 * pLoadUserPath, UINT32 * version, UINT64 * node_count ) { nlog_dict_entry* head = NULL; CHAR8** string_splits = NULL; CHAR8** file_lines = NULL; CHAR8* file_buffer = NULL; UINT64 bytes_read; UINT64 found_elements = 0; UINT64 line_count = 0; UINT32 x = 0; EFI_DEVICE_PATH_PROTOCOL *pDevicePathProtocol = NULL; EFI_STATUS status; PRINT_CONTEXT *pPrinterCtx = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; if (pCmd != NULL) { pPrinterCtx = pCmd->pPrintCtx; } *node_count = 0; CHAR16 * pDictPath = AllocateZeroPool(OPTION_VALUE_LEN * sizeof(*pDictPath)); if (pDictPath == NULL) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_OUT_OF_MEMORY); return NULL; } status = GetDeviceAndFilePath(pLoadUserPath, pDictPath, &pDevicePathProtocol); if (EFI_ERROR(status)) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to locate the file: " FORMAT_STR L" %lu\n", pLoadUserPath, status); goto Finish; } status = FileRead(pDictPath, pDevicePathProtocol, MAX_CONFIG_DUMP_FILE_SIZE, FALSE, &bytes_read, (VOID **)&file_buffer); if (EFI_ERROR(status) || NULL == file_buffer) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to open or read the file: " FORMAT_STR L" %lu\n", pLoadUserPath, status); goto Finish; } while (bytes_read > 0 && (file_buffer[bytes_read - 1] == '\n' || file_buffer[bytes_read - 1] == '\r' || file_buffer[bytes_read - 1] == '\t' || file_buffer[bytes_read - 1] == ' ')) { file_buffer[bytes_read - 1] = 0; bytes_read--; } file_lines = string_split(file_buffer, '\n', 0, &line_count); if (NULL == file_lines || line_count <= 1) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Dictionary passed does not contain enough content.\n"); goto Finish; } string_splits = string_split(file_lines[0], NLOG_DICT_VERSION_SPLIT_CHAR, 2, &found_elements); if (NULL == string_splits) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Error in dict on line 1 - Found 0 elements, expected %lu\n", 2); goto Finish; } if (found_elements != 2) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Error in dict on line 1 - Found %lu elements, expected %lu\n", found_elements, 2); goto Finish; } *version = a_to_u32(string_splits[1]); if (*version != 2) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Only version 2 dictionaries supported.\n"); goto Finish; } head = load_nlog_dict_v2(pCmd, &file_lines[1], (line_count - 1), node_count); Finish: if (NULL != string_splits) { for (x = 0; x < found_elements; x++) { FREE_POOL_SAFE(string_splits[x]); } FREE_POOL_SAFE(string_splits); } if (NULL != file_lines) { for (x = 0; x < line_count; x++) { FREE_POOL_SAFE(file_lines[x]); } FREE_POOL_SAFE(file_lines); } FREE_POOL_SAFE(file_buffer); return head; } nlog_dict_entry* load_nlog_dict_v2( struct Command *pCmd, CHAR8 ** lines, UINT64 line_count, UINT64 * node_count ) { UINT32 x = 0; nlog_dict_entry* head = NULL; nlog_dict_entry* tail = NULL; CHAR8* line = NULL; UINT64 found_elements = 0; CHAR8** parts = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; if (pCmd != NULL) { pPrinterCtx = pCmd->pPrintCtx; } *node_count = 0; for (x = 0; x < line_count; x++) { if (!lines[x]) { break; } line = lines[x]; parts = string_split(line, NLOG_DICT_SPLIT_CHAR, NLOG_DICT_FIELDCOUNT, &found_elements); if (NULL == parts) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Error in dict on line %lu - Found NULL elements, expected %lu.\n", x + 1, NLOG_DICT_FIELDCOUNT); goto Finish; } if (found_elements != NLOG_DICT_FIELDCOUNT) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Error in dict on line %lu - Found %lu elements, expected %lu.\n", x + 1, found_elements, NLOG_DICT_FIELDCOUNT); goto Finish; } *node_count = *node_count + 1; if (head == NULL) { head = AllocateZeroPool(sizeof(nlog_dict_entry)); if (NULL == head) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } tail = head; tail->prev = NULL; tail->next = NULL; } else { tail->next = AllocateZeroPool(sizeof(nlog_dict_entry)); if (NULL == tail->next) { PRINTER_SET_MSG(pPrinterCtx, ReturnCode, L"Failed to allocate space for decoded records\n"); goto Finish; } ((nlog_dict_entry*)tail->next)->prev = tail; tail = tail->next; } tail->Hash = a_to_u32(parts[0]); tail->Args = a_to_u32(parts[1]); FREE_POOL_SAFE(parts[0]); FREE_POOL_SAFE(parts[1]); tail->LogLevel = parts[2]; tail->FileName = parts[3]; tail->LogString = parts[4]; tail->next = NULL; FREE_POOL_SAFE(parts); } Finish: if (parts != NULL) { FREE_POOL_SAFE(parts[0]); FREE_POOL_SAFE(parts[1]); FREE_POOL_SAFE(parts); } return head; } ipmctl-03.00.00.0485/DcpmPkg/common/Nlog.h000066400000000000000000000051511440615110200175020ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _NLOG_H_ #define _NLOG_H_ #include "Strings.h" #define NLOG_DICT_FIELDCOUNT 5 #define NLOG_DICT_SPLIT_CHAR ',' #define NLOG_DICT_VERSION_SPLIT_CHAR '=' typedef union { struct { UINT32 magic_number : 24; UINT32 version : 8; } data; UINT32 rawData; } nlog_version_v2; typedef union { struct { UINT32 args : 8; UINT32 line_number : 16; UINT32 module_id : 8; } data; UINT32 rawData; } nlog_version_v1; typedef struct { UINT32 Hash; UINT64 Args; CHAR8* LogLevel; CHAR8* FileName; CHAR8* LogString; VOID* next; VOID* prev; } nlog_dict_entry; typedef struct { UINT64 id; UINT32 KernelTime; UINT32** ArgValues; nlog_dict_entry* DictEntry; CHAR8* FormattedString; UINT64 FormattedStringLen; VOID* next; VOID* prev; } nlog_record; /* decode_nlog_binary command @param[in] decoded_file_name - the file to append records to @param[in] nlogbytes - the blob returned from the dump command @param[in] size - the number of bytes in the blob @param[in] dict_version - the version of the loaded dictionary @param[in] dict_head - the head to the dictionary linked list @param[out] node_count - the number of decoded entries */ VOID decode_nlog_binary( struct Command *pCmd, CHAR16* decoded_file_name, UINT8* nlogbytes, UINT64 size, UINT32 dict_version, nlog_dict_entry* dict_head ); /* get_nlog_entry command @param[in] hashVal - the hash to locate @param[in] head - the head to the dictionary linked list @retval the discovered node, or NULL */ nlog_dict_entry* get_nlog_entry( IN UINT32 hashVal, IN nlog_dict_entry* head ); /* load_nlog_dict command @param[in] pDictPath - the path to the dictionary file @param[out] version - the version of the dictionary as detected @param[out] node_count - the number of nodes in the linked list @retval the head to the dictionary linked list */ nlog_dict_entry* load_nlog_dict( struct Command *pCmd, IN CHAR16 * pDictPath, OUT UINT32 * version, OUT UINT64 * node_count ); /* load_nlog_dict_v2 command @param[in] lines - the array of strings to convert to structs @param[in] line_count - the length of the array of strings to convert to structs @param[out] node_count - the number of nodes in the linked list @retval the head to the dictionary linked list */ nlog_dict_entry* load_nlog_dict_v2( struct Command *pCmd, IN CHAR8 ** lines, IN UINT64 line_count, OUT UINT64 * node_count ); /* Loads test binary dumps for the purpose of decoding them */ VOID ** LoadBinaryFile( CHAR16 * pLoadUserPath, OUT UINT64 *bytes_read); #endif ipmctl-03.00.00.0485/DcpmPkg/common/NvmHealth.c000066400000000000000000000236151440615110200204710ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include "NvmHealth.h" #include /** Init sensors array with default values @param[in,out] DimmSensorsSet Sensors array to fill with default values **/ VOID InitSensorsSet( IN OUT DIMM_SENSOR DimmSensorsSet[SENSOR_TYPE_COUNT] ) { UINT8 Index = 0; if (DimmSensorsSet == NULL) { return; } ZeroMem(DimmSensorsSet, sizeof(DIMM_SENSOR) * SENSOR_TYPE_COUNT); for (Index = 0; Index < SENSOR_TYPE_COUNT; ++Index) { DimmSensorsSet[Index].Type = Index; DimmSensorsSet[Index].Enabled = SENSOR_NA_ENABLED; DimmSensorsSet[Index].SettableThresholds = ThresholdNone; DimmSensorsSet[Index].SupportedThresholds = ThresholdNone; } DimmSensorsSet[SENSOR_TYPE_CONTROLLER_TEMPERATURE].SettableThresholds = AlarmThreshold; DimmSensorsSet[SENSOR_TYPE_CONTROLLER_TEMPERATURE].SupportedThresholds = AlarmThreshold | ShutdownThreshold; DimmSensorsSet[SENSOR_TYPE_MEDIA_TEMPERATURE].SettableThresholds = AlarmThreshold; DimmSensorsSet[SENSOR_TYPE_MEDIA_TEMPERATURE].SupportedThresholds = AlarmThreshold | ThrottlingStopThreshold | ThrottlingStartThreshold | ShutdownThreshold; DimmSensorsSet[SENSOR_TYPE_PERCENTAGE_REMAINING].SettableThresholds = AlarmThreshold; DimmSensorsSet[SENSOR_TYPE_PERCENTAGE_REMAINING].SupportedThresholds = AlarmThreshold; } EFI_STATUS GetSensorsInfo( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN UINT16 DimmID, IN OUT DIMM_SENSOR DimmSensorsSet[SENSOR_TYPE_COUNT] ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT8 Index = 0; SMART_AND_HEALTH_INFO HealthInfo; INT16 Threshold = 0; UINT8 DimmHealthState = 0; ZeroMem(&HealthInfo, sizeof(HealthInfo)); /** Driver fills the data partially, so the initializer stays with the proper sensor types and default data. **/ InitSensorsSet(DimmSensorsSet); ReturnCode = pNvmDimmConfigProtocol->GetSmartAndHealth(pNvmDimmConfigProtocol, DimmID, &HealthInfo); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Copy SMART & Health values **/ DimmSensorsSet[SENSOR_TYPE_MEDIA_TEMPERATURE].Value = HealthInfo.MediaTemperature; DimmSensorsSet[SENSOR_TYPE_MEDIA_TEMPERATURE].ThrottlingStopThreshold = HealthInfo.MediaThrottlingStopThresh; DimmSensorsSet[SENSOR_TYPE_MEDIA_TEMPERATURE].ThrottlingStartThreshold = HealthInfo.MediaThrottlingStartThresh; DimmSensorsSet[SENSOR_TYPE_MEDIA_TEMPERATURE].ShutdownThreshold = HealthInfo.MediaTempShutdownThresh; DimmSensorsSet[SENSOR_TYPE_MEDIA_TEMPERATURE].MaxTemperature = HealthInfo.MaxMediaTemperature; DimmSensorsSet[SENSOR_TYPE_CONTROLLER_TEMPERATURE].Value = HealthInfo.ControllerTemperature; DimmSensorsSet[SENSOR_TYPE_CONTROLLER_TEMPERATURE].ShutdownThreshold = HealthInfo.ContrTempShutdownThresh; DimmSensorsSet[SENSOR_TYPE_CONTROLLER_TEMPERATURE].ThrottlingStopThreshold = HealthInfo.ControllerThrottlingStopThresh; DimmSensorsSet[SENSOR_TYPE_CONTROLLER_TEMPERATURE].ThrottlingStartThreshold = HealthInfo.ControllerThrottlingStartThresh; DimmSensorsSet[SENSOR_TYPE_CONTROLLER_TEMPERATURE].MaxTemperature = HealthInfo.MaxControllerTemperature; DimmSensorsSet[SENSOR_TYPE_PERCENTAGE_REMAINING].Value = HealthInfo.PercentageRemaining; DimmSensorsSet[SENSOR_TYPE_POWER_CYCLES].Value = HealthInfo.PowerCycles; DimmSensorsSet[SENSOR_TYPE_POWER_ON_TIME].Value = HealthInfo.PowerOnTime; DimmSensorsSet[SENSOR_TYPE_LATCHED_DIRTY_SHUTDOWN_COUNT].Value = HealthInfo.LatchedDirtyShutdownCount; DimmSensorsSet[SENSOR_TYPE_UNLATCHED_DIRTY_SHUTDOWN_COUNT].Value = HealthInfo.UnlatchedDirtyShutdownCount; DimmSensorsSet[SENSOR_TYPE_FW_ERROR_COUNT].Value = HealthInfo.MediaErrorCount + HealthInfo.ThermalErrorCount; DimmSensorsSet[SENSOR_TYPE_UP_TIME].Value = HealthInfo.UpTime; /** Determine Health State based on Health Status Bit Mask **/ ConvertHealthBitmask(HealthInfo.HealthStatus, &DimmHealthState); DimmSensorsSet[SENSOR_TYPE_DIMM_HEALTH].Value = DimmHealthState; for (Index = SENSOR_TYPE_MEDIA_TEMPERATURE; Index <= SENSOR_TYPE_PERCENTAGE_REMAINING; ++Index) { ReturnCode = pNvmDimmConfigProtocol->GetAlarmThresholds( pNvmDimmConfigProtocol, DimmID, Index, &Threshold, &DimmSensorsSet[Index].Enabled, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } DimmSensorsSet[Index].AlarmThreshold = Threshold; } Finish: return ReturnCode; } /** Translate the SensorType into its Unicode string representation. The string buffer is static and the returned string is const so the caller should not make changes to the returned buffer. @param[in] SensorType The enum sensor type. The SensorTypeAll will result in an "Unknown" return as this value is not translatable. **/ CONST CHAR16 * SensorTypeToString( IN UINT8 SensorType ) { switch (SensorType) { case SENSOR_TYPE_MEDIA_TEMPERATURE: return MEDIA_TEMPERATURE_STR; case SENSOR_TYPE_CONTROLLER_TEMPERATURE: return CONTROLLER_TEMPERATURE_STR; case SENSOR_TYPE_PERCENTAGE_REMAINING: return SPARE_CAPACITY_STR; case SENSOR_TYPE_POWER_CYCLES: return POWER_CYCLES_STR; case SENSOR_TYPE_POWER_ON_TIME: return POWER_ON_TIME_STR; case SENSOR_TYPE_LATCHED_DIRTY_SHUTDOWN_COUNT: return LATCHED_DIRTY_SHUTDOWN_COUNT_STR; case SENSOR_TYPE_FW_ERROR_COUNT: return FW_ERROR_COUNT_STR; case SENSOR_TYPE_UP_TIME: return UPTIME_STR; case SENSOR_TYPE_DIMM_HEALTH: return DIMM_HEALTH_STR; case SENSOR_TYPE_UNLATCHED_DIRTY_SHUTDOWN_COUNT: return UNLATCHED_DIRTY_SHUTDOWN_COUNT_STR; default: return L"Unknown"; } } /** Assign unit of measure for each SensorType. @param[in] SensorType The enum sensor type. Default case provides scalar. **/ CONST CHAR16 * SensorValueMeasure( IN UINT8 SensorType ) { switch(SensorType) { case SENSOR_TYPE_MEDIA_TEMPERATURE: case SENSOR_TYPE_CONTROLLER_TEMPERATURE: return TEMPERATURE_MSR; case SENSOR_TYPE_PERCENTAGE_REMAINING: return SPARE_CAPACITY_MSR; case SENSOR_TYPE_POWER_ON_TIME: case SENSOR_TYPE_UP_TIME: return TIME_MSR; default: return L""; } } /** Translate the SensorThresholdsType into its Unicode string representation. A returned string has to be freed by the caller. **/ CHAR16 * SensorThresholdsToString( IN SensorThresholds SensorThresholdsType ) { CHAR16 *pStr = NULL; CHAR16 *pFormat = NULL; if (SensorThresholdsType == ThresholdNone) { pStr = CatSPrintClean(pStr, FORMAT_STR, THRESHOLD_NONE_STR); } else { if ((SensorThresholdsType & AlarmThreshold) != 0) { pFormat = (pStr == NULL) ? FORMAT_STR : L"," FORMAT_STR; pStr = CatSPrintClean(pStr, pFormat, THRESHOLD_ALARM_STR); } if ((SensorThresholdsType & ThrottlingStopThreshold) != 0) { pFormat = (pStr == NULL) ? FORMAT_STR : L"," FORMAT_STR; pStr = CatSPrintClean(pStr, pFormat, THRESHOLD_THROTTLING_STOP_STR); } if ((SensorThresholdsType & ThrottlingStartThreshold) != 0) { pFormat = (pStr == NULL) ? FORMAT_STR : L"," FORMAT_STR; pStr = CatSPrintClean(pStr, pFormat, THRESHOLD_THROTTLING_START_STR); } if ((SensorThresholdsType & ShutdownThreshold) != 0) { pFormat = (pStr == NULL) ? FORMAT_STR : L"," FORMAT_STR; pStr = CatSPrintClean(pStr, pFormat, THRESHOLD_SHUTDOWN_STR); } } return pStr; } /** Translate the Enabled into its Unicode string representation. The string buffer is static and the returned string is const so the caller should not make changes to the returned buffer. **/ CONST CHAR16 * SensorEnabledStateToString( IN UINT8 SensorState ) { switch (SensorState) { case SENSOR_ENABLED: return SENSOR_ENABLED_STATE_ENABLED_STR; case SENSOR_DISABLED: return SENSOR_ENABLED_STATE_DISABLED_STR; case SENSOR_NA_ENABLED: return NOT_APPLICABLE_SHORT_STR; default: return L"Unknown"; } } /** Convert Health state bitmask to a defined state @param[in] HealthMask - mask from DIMM structure @param[out] pHealthState - pointer to output with defined Health State **/ VOID ConvertHealthBitmask( IN UINT8 HealthMask, OUT UINT8 *pHealthState ) { if (HealthMask & HealthStatusFatal) { *pHealthState = HEALTH_FATAL_FAILURE; } else if (HealthMask & HealthStatusCritical) { *pHealthState = HEALTH_CRITICAL_FAILURE; } else if (HealthMask & HealthStatusNoncritical) { *pHealthState = HEALTH_NON_CRITICAL_FAILURE; } else if (HealthMask == CONTROLLER_HEALTH_NORMAL) { *pHealthState = HEALTH_HEALTHY; } else { *pHealthState = HEALTH_UNKNOWN; } } /** Convert dimm or sensor health state to a string. The caller is responsible for freeing the returned string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] Health State - Numeric Value of the Health State. Defined in NvmTypes.h @retval String Representation of the health state **/ EFI_STRING HealthToString( IN EFI_HANDLE HiiHandle, IN UINT8 HealthState ) { switch (HealthState) { case HEALTH_HEALTHY: return HiiGetString(HiiHandle, STRING_TOKEN(STR_HEALTHY), NULL); case HEALTH_NON_CRITICAL_FAILURE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_NON_CRITICAL_FAILURE), NULL); case HEALTH_CRITICAL_FAILURE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_CRITICAL_FAILURE), NULL); case HEALTH_FATAL_FAILURE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_FATAL_FAILURE), NULL); case HEALTH_UNMANAGEABLE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_UNMANAGEABLE), NULL); case HEALTH_NON_FUNCTIONAL: return HiiGetString(HiiHandle, STRING_TOKEN(STR_NON_FUNCTIONAL), NULL); case HEALTH_UNKNOWN: default: return HiiGetString(HiiHandle, STRING_TOKEN(STR_UNKNOWN), NULL); } } ipmctl-03.00.00.0485/DcpmPkg/common/NvmHealth.h000066400000000000000000000157101440615110200204730ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file NvmHealth.h * @brief Health Types for EFI_DCPMM_CONFIG2_PROTOCOL to configure and manage PMem modules. */ #ifndef _NVMHEALTH_H_ #define _NVMHEALTH_H_ #include "NvmInterface.h" #define SPARE_CAPACITY_STR L"PercentageRemaining" #define CONTROLLER_TEMPERATURE_STR L"ControllerTemperature" #define MEDIA_TEMPERATURE_STR L"MediaTemperature" #define POWER_ON_TIME_STR L"PowerOnTime" #define LATCHED_DIRTY_SHUTDOWN_COUNT_STR L"LatchedDirtyShutdownCount" #define POWER_CYCLES_STR L"PowerCycles" #define UPTIME_STR L"UpTime" #define FW_ERROR_COUNT_STR L"FwErrorCount" #define DIMM_HEALTH_STR L"Health" #define UNLATCHED_DIRTY_SHUTDOWN_COUNT_STR L"UnlatchedDirtyShutdownCount" #define MAX_MEDIA_TEMPERATURE_STR L"MaxMediaTemperature" #define MAX_CONTROLLER_TEMPERATURE_STR L"MaxControllerTemperature" #define SENSORS_COMBINED \ L"Health|MediaTemperature|ControllerTemperature|PercentageRemaining|LatchedDirtyShutdownCount|PowerOnTime|" \ L"UpTime|PowerCycles|FwErrorCount|UnlatchedDirtyShutdownCount|MaxMediaTemperature|MaxControllerTemperature" \ #define TEMPERATURE_MSR L"C" #define SPARE_CAPACITY_MSR L"%" #define TIME_MSR L"s" #define TIME_MSR_MS L"ms" #define THRESHOLD_NONE_STR L"None" #define THRESHOLD_ALARM_STR L"AlarmThreshold" #define THRESHOLD_THROTTLING_STOP_STR L"ThrottlingStopThreshold" #define THRESHOLD_THROTTLING_START_STR L"ThrottlingStartThreshold" #define THRESHOLD_SHUTDOWN_STR L"ShutdownThreshold" typedef enum { ThresholdNone = 0, AlarmThreshold = BIT0, ThrottlingStopThreshold = BIT1, ThrottlingStartThreshold = BIT2, ShutdownThreshold = BIT3 } SensorThresholds; #define SENSOR_ENABLED_STATE_ENABLED_STR L"1" #define SENSOR_ENABLED_STATE_DISABLED_STR L"0" #define NOT_APPLICABLE_SHORT_STR L"N/A" #define EXTENDED_ADR_FLUSH_COMPLETE 0b1111 #define SX_EXTENDED_FLUSH_COMPLETE 0b1111 /** Namespace PM Capable values **/ #define NAMESPACE_PM_CAPABLE_BLOCK_MODE_ONLY 0 #define NAMESPACE_PM_CAPABLE_PERSISTENT_MEM 1 #define TEMPERATURE_POSITIVE 0 #define TEMPERATURE_NEGATIVE 1 #define CONTROLLER_HEALTH_NORMAL 0 /** Overall PMem module Health Status */ enum HEALTH_STATUS { HealthStatusNoncritical = BIT0, //!< Non-Critical (maintenance required) HealthStatusCritical = BIT1, //!< Critical (features or performance degraded due to failure) HealthStatusFatal = BIT2 //!< Fatal (data loss has occurred or is imminent) }; #pragma pack(push) #pragma pack(1) /** Last shutdown status struct **/ typedef union { UINT8 AllFlags; struct { UINT8 PmAdr : 1; //!< PM ADR Command received UINT8 PmS3 : 1; //!< PM Se received UINT8 PmS5 : 1; //!< PM S5 received UINT8 DdrtPowerFailure : 1; //!< DDRT Power Fail Command received UINT8 PmicPowerLoss : 1; //!< PMIC Power Loss UINT8 PmWarmReset : 1; //!< PM Warm Reset received UINT8 ThermalShutdown : 1; //!< Thermal Shutdown received UINT8 FwFlushComplete : 1; //!< Flush Complete } Separated; } LAST_SHUTDOWN_STATUS_DETAILS; /** Last shutdown status extended struct **/ typedef union { UINT8 Raw[3]; struct { UINT16 ViralInterrupt : 1; //!< Viral interrupt received UINT16 SurpriseClockStopInterrupt : 1; //!< Surprise clock stop interrupt received UINT16 WriteDataFlushComplete : 1; //!< Write Data Flush Complete UINT16 S4PowerState : 1; //!< S4 Power State received UINT16 PMIdle : 1; //!< PM Idle or SRE Clock Stop received UINT16 DdrtSurpriseReset : 1; //!< Surprise Reset received UINT16 EnhancedAdrFlushStatus : 4; //!< eADR Flush Status UINT16 SxExtendedFlushStatus : 4; //!< Sx Extended Flush Status } Separated; } LAST_SHUTDOWN_STATUS_DETAILS_EXTENDED; /** Last shutdown status combined struct NvmDimmConfig combines the structs into one uint32 for CLI/HII **/ typedef union { UINT32 AsUint32; struct { LAST_SHUTDOWN_STATUS_DETAILS LastShutdownStatus; LAST_SHUTDOWN_STATUS_DETAILS_EXTENDED LastShutdownStatusExtended; } Combined; } LAST_SHUTDOWN_STATUS_DETAILS_COMBINED; /** Temperature structure **/ typedef union { UINT16 AsUint16; struct { UINT16 TemperatureValue : 15; //< Unsigned, integer temperature in Celsius UINT16 Sign : 1; //< Sign Bit (1 - negative, 0 - positive) } Separated; } TEMPERATURE; #pragma pack(pop) /** Init sensors array with default values @param[in,out] DimmSensorsSet Sensors array to fill with default values **/ VOID InitSensorsSet( IN OUT DIMM_SENSOR DimmSensorsSet[SENSOR_TYPE_COUNT] ); EFI_STATUS GetSensorsInfo( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN UINT16 DimmID, IN OUT DIMM_SENSOR DimmSensorsSet[SENSOR_TYPE_COUNT] ); /** Translate the SensorType into its Unicode string representation. The string buffer is static and the returned string is const so the caller should not make changes to the returned buffer. @param[in] SensorType The enum sensor type. The SensorTypeAll will result in an "Unknown" return as this value is not translatable. **/ CONST CHAR16 * SensorTypeToString( IN UINT8 SensorType ); /** Assign unit of measure for each SensorType. @param[in] SensorType The enum sensor type. Default case provides scalar. **/ CONST CHAR16 * SensorValueMeasure( IN UINT8 SensorType ); /** Translate the SensorThresholdsType into its Unicode string representation. A returned string has to be freed by the caller. **/ CHAR16 * SensorThresholdsToString( IN SensorThresholds SensorThresholdsType ); /** Translate the EnabledState into its Unicode string representation. The string buffer is static and the returned string is const so the caller should not make changes to the returned buffer. **/ CONST CHAR16 * SensorEnabledStateToString( IN UINT8 SensorState ); /** Convert Health state bitmask to a defined state @param[in] HealthMask - mask from PMem module structure @param[out] pHealthState - pointer to output with defined Health State **/ VOID ConvertHealthBitmask( IN UINT8 HealthMask, OUT UINT8 *pHealthState ); /** Convert PMem module or sensor health state to a string. The caller is responsible for freeing the returned string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] HealthState - Numeric Value of the Health State. Defined in NvmTypes.h @retval String Representation of the health state **/ EFI_STRING HealthToString( IN EFI_HANDLE HiiHandle, IN UINT8 HealthState ); #endif /** _NVMHEALTH_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/NvmInterface.h000066400000000000000000001732241440615110200211730ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file NvmInterface.h * @brief Implementation of the EFI_DCPMM_CONFIG2_PROTOCOL, a custom protocol * to configure and manage Intel Optane persistent memory modules */ #ifndef _NVM_INTERFACE_H_ #define _NVM_INTERFACE_H_ #include #include "NvmStatus.h" #ifndef OS_BUILD #include "Dcpmm.h" #endif #include "NvmTypes.h" #include "NvmTables.h" #include #include // Auto = no restrictions typedef enum _TRANSPORT_PROTOCOL { FisTransportSmbus = 0, FisTransportDdrt = 1, FisTransportAuto = 2 } TRANSPORT_PROTOCOL; // Auto = no restrictions typedef enum _TRANSPORT_PAYLOAD_SIZE { FisTransportSizeSmallMb = 0, FisTransportSizeLargeMb = 1, FisTransportSizeAuto = 2 } TRANSPORT_PAYLOAD_SIZE; typedef struct _EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS { TRANSPORT_PROTOCOL Protocol; TRANSPORT_PAYLOAD_SIZE PayloadSize; } EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS; /** Resolves to TRUE if the "-smbus" flag was passed in via CLI or equivalent. Restricts all communications to smbus only. FALSE otherwise. **/ #define IS_SMBUS_FLAG_ENABLED(TransportAttribs) (FisTransportSmbus == TransportAttribs.Protocol) /** Resolves to TRUE if the "-ddrt" flag was passed in via CLI or equivalent. Restricts all communications to DDRT only. FALSE otherwise. **/ #define IS_DDRT_FLAG_ENABLED(TransportAttribs) (FisTransportDdrt == TransportAttribs.Protocol) /** Resolves to TRUE if the "-spmb" flag was passed in via CLI or equivalent. Restricts all communications to small payload mailbox only. FALSE otherwise. **/ #define IS_SMALL_PAYLOAD_FLAG_ENABLED(TransportAttribs) (FisTransportSizeSmallMb == TransportAttribs.PayloadSize) /** Resolves to TRUE if the "-lpmb" flag was passed in via CLI or equivalent. Restricts all communications to large payload mailbox only. FALSE otherwise. **/ #define IS_LARGE_PAYLOAD_FLAG_ENABLED(TransportAttribs) (FisTransportSizeLargeMb == TransportAttribs.PayloadSize) #define MAX_NO_OF_DIAGNOSTIC_SUBTESTS 5 #define EFI_DCPMM_CONFIG2_PROTOCOL_GUID \ {0xd5a7cf05, 0x8ec1, 0x48cb, {0x95, 0x94, 0x20, 0x52, 0xf9, 0xbd, 0x11, 0x56}} #define NVMDIMM_DRIVER_DEVICE_PATH_GUID \ { 0xb976a9d2, 0x8772, 0x414f, {0x9f, 0xb0, 0x05, 0x99, 0x95, 0xf4, 0xbe, 0xac}} #define EFI_DCPMM_PBR_PROTOCOL_GUID \ {0x8761c5cc, 0x5dfc, 0x40cc, {0x89, 0xc9, 0xb6, 0x35, 0xea, 0x99, 0x23, 0x9f}} typedef struct _EFI_DCPMM_CONFIG2_PROTOCOL EFI_DCPMM_CONFIG2_PROTOCOL; typedef struct _EFI_DCPMM_PBR_PROTOCOL EFI_DCPMM_PBR_PROTOCOL; /** Retrieve the number of PMem modules in the system found in NFIT @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pDimmCount The number of PMem modules found in NFIT. @retval EFI_SUCCESS The count was returned properly @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_DIMM_COUNT) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pDimmCount ); /** Retrieve the number of uninitialized PMem modules in the system found through SMBUS @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pDimmCount The number of PMem modules found through SMBUS. @retval EFI_SUCCESS The count was returned properly @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_UNINITIALIZED_DIMM_COUNT) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pDimmCount ); /** Retrieve the list of PMem modules found in NFIT @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmCount The size of pDimms. @param[in] dimmInfoCategories The categories of additional PMem module info parameters to retrieve @param[out] pDimms The PMem module list found in NFIT. @retval EFI_SUCCESS The PMem module list was returned properly @retval EFI_INVALID_PARAMETER one or more parameters are NULL. @retval EFI_NOT_FOUND PMem module not found **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_DIMMS) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT32 DimmCount, IN DIMM_INFO_CATEGORIES dimmInfoCategories, OUT DIMM_INFO *pDimms ); /** Retrieve the list of uninitialized PMem modules found through SMBUS @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmCount The size of pDimms. @param[out] pDimms The PMem module list found through SMBUS. @retval EFI_SUCCESS The PMem module list was returned properly @retval EFI_INVALID_PARAMETER one or more parameter are NULL. @retval EFI_NOT_FOUND PMem module not found **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_UNINITIALIZED_DIMMS) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT32 DimmCount, OUT DIMM_INFO *pDimms ); /** Retrieve the details about the PMem module specified with pid found in NFIT @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Pid The ID of the PMem module to retrieve @param[in] dimmInfoCategories The categories of additional PMem module info parameters to retrieve @param[out] pDimmInfo A pointer to the PMem module found in NFIT @retval EFI_SUCCESS The PMem module information was returned properly @retval EFI_INVALID_PARAMETER pDimm is NULL or the PMem module with the pid provided does not exist. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_DIMM) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 Pid, IN DIMM_INFO_CATEGORIES dimmInfoCategories, OUT DIMM_INFO *pDimmInfo ); #ifdef OS_BUILD /** Get the PMON registers @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Pid The ID of the PMem module to retrieve @param[in] dimmInfoCategories The categories of additional PMem module info parameters to retrieve @param[out] pDimmInfo A pointer to the PMem module found in NFIT @retval EFI_SUCCESS The PMem module information was returned properly @retval EFI_INVALID_PARAMETER pDimm is NULL or the PMem module with the pid provided does not exist. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_PMON) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 Pid, IN UINT8 SmartDataMask, OUT PMON_REGISTERS *pPayloadPMONRegisters ); /** Set the PMON Group @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Pid The ID of the PMem module to retrieve @param[in] dimmInfoCategories The categories of additional PMem module info parameters to retrieve @param[out] pDimmInfo A pointer to the PMem module found in NFIT @retval EFI_SUCCESS The PMem module information was returned properly @retval EFI_INVALID_PARAMETER pDimm is NULL or the PMem module with the pid provided does not exist. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_SET_PMON) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 Pid, IN UINT8 PMONGroupEnable ); #endif /** Retrieve the details about the uninitialized PMem module specified with pid found through SMBUS @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Pid The ID of the PMem module to retrieve @param[out] pDimmInfo A pointer to the PMem module found through SMBUS @retval EFI_SUCCESS The PMem module information was returned properly @retval EFI_INVALID_PARAMETER pDimm is NULL or the PMem module with the pid provided does not exist. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_UNINITIALIZED_DIMM) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 Pid, OUT DIMM_INFO *pDimmInfo ); /** Retrieve the list of sockets (physical processors) in the host server @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pSocketCount The size of the list of sockets. @param[out] ppSockets Pointer to the list of sockets. @retval EFI_SUCCESS The socket list was returned properly or, the platform does not support socket sku limits @retval EFI_INVALID_PARAMETER One or more parameters are NULL. @retval EFI_NOT_FOUND PCAT tables could not be retrieved successfully @retval EFI_DEVICE_ERROR Internal function error @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_SOCKETS) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pSocketCount, OUT SOCKET_INFO **ppSockets ); /** Retrieve an SMBIOS table type 17 table for a specific PMem module @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Pid The ID of the PMem module to retrieve @param[out] pTable A pointer to the SMBIOS table @retval EFI_SUCCESS The count was returned properly @retval EFI_INVALID_PARAMETER pTable is NULL @retval EFI_INVALID_PARAMETER PMem module pid is not valid. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_DIMM_SMBIOS_TABLE) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 Pid, IN UINT8 Type, OUT SMBIOS_STRUCTURE_POINTER *pTable ); /** Retrieve the NFIT ACPI table @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] ppNFit A pointer to the output NFIT table @retval EFI_SUCCESS Ok @retval EFI_OUT_OF_RESOURCES Problem with allocating memory @retval EFI_NOT_FOUND PCAT tables not found @retval EFI_INVALID_PARAMETER pNFit is NULL **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_ACPI_NFIT) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT ParsedFitHeader **ppNFit ); /** Retrieve the PCAT ACPI table @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] ppPcat output buffer with PCAT tables @retval EFI_SUCCESS Ok @retval EFI_OUT_OF_RESOURCES Problem with allocating memory @retval EFI_NOT_FOUND PCAT tables not found **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_ACPI_PCAT) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT ParsedPcatHeader **pPcat ); /** Retrieve the PMTT ACPI table @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] ppPMTT output buffer with PMTT tables @retval EFI_SUCCESS Ok @retval EFI_OUT_OF_RESOURCES Problem with allocating memory @retval EFI_NOT_FOUND PCAT tables not found **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_ACPI_PMTT) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT VOID **pPMTT ); /** Get Platform Config Data The caller is responsible for freeing ppDimmPcdInfo by using FreeDimmPcdInfoArray. @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param{in] PcdTarget Target PCD partition: ALL=0, CONFIG=1, NAMESPACES=2 @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[out] ppDimmPcdInfo Pointer to output array of PCDs @param[out] pDimmPcdInfoCount Number of items in PMem module PCD Info @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more input parameters are NULL @retval EFI_NO_RESPONSE FW busy for one or more PMem modules @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_PCD) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT8 PcdTarget, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, OUT DIMM_PCD_INFO **ppDimmPcdInfo, OUT UINT32 *pDimmPcdInfoCount, OUT COMMAND_STATUS *pCommandStatus ); /** Clear PCD configs @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] ConfigIdMask Bitmask that defines which config to delete @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more input parameters are NULL @retval EFI_NO_RESPONSE FW busy for one or more PMem modules @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_DELETE_PCD_CONFIG) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT32 ConfigIdMask, OUT COMMAND_STATUS *pCommandStatus ); /** Check NVM device security state Function checks security state of a set of PMem modules. It sets security state to mixed when not all PMem modules have the same state. @param[in] pThis a pointer to EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[out] pSecurityState security state of a PMem module or all PMem modules @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER when pSecurityState is NULL @retval EFI_NOT_FOUND it was not possible to get state of a PMem module @retval EFI_SUCCESS state correctly detected and stored in pSecurityState **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_SECURITY_STATE) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, OUT UINT8 *pSecurityState, OUT COMMAND_STATUS *pCommandStatus ); /** Set NVM device security state. Function sets security state on a set of PMem modules. If there is a failure on one of PMem modules function continues with setting state on following PMem modules but exits with error. @param[in] pThis a pointer to EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] pDimmIds Pointer to an array of PMem module IDs - if NULL, execute operation on all PMem modules @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] SecurityOperation Security Operation code @param[in] pPassphrase a pointer to string with current passphrase @param[in] pNewPassphrase a pointer to string with new passphrase @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER when pLockState is NULL @retval EFI_OUT_OF_RESOURCES couldn't allocate memory for a structure @retval EFI_UNSUPPORTED LockState to be set is not recognized, or mixed sku of PMem modules is detected @retval EFI_DEVICE_ERROR setting state for a PMem module failed @retval EFI_NOT_FOUND a PMem module was not found @retval EFI_NO_RESPONSE FW busy for one or more PMem modules @retval EFI_SUCCESS security state correctly set **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_SET_SECURITY_STATE) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 SecurityOperation, IN CHAR16 *pPassphrase, IN CHAR16 *pNewPassphrase, OUT COMMAND_STATUS *pCommandStatus ); /** Gather info about total capacities on all PMem modules @param[in] pThis a pointer to EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] UseNfit flag to indicate NFIT usage @param[out] pMemoryResourcesInfo structure filled with required information @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_ABORTED PCAT tables not found @retval Other errors failure of FW commands @retval EFI_SUCCESS Success @retval EFI_NO_RESPONSE FW busy on one or more PMem modules **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_REGION_COUNT) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN BOOLEAN UseNfit, OUT UINT32 *pCount ); /** Retrieve the region list @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Count The number of regions. @param[in] UseNfit flag to indicate NFIT usage @param[out] pRegions The region list @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_SUCCESS The region list was returned properly @retval EFI_INVALID_PARAMETER pRegions is NULL. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_REGIONS) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT32 Count, IN BOOLEAN UseNfit, OUT struct _REGION_INFO *pRegions, OUT COMMAND_STATUS *pCommandStatus ); /** Retrieve the details about the region specified with region id @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] RegionId The region id of the region to retrieve @param[out] pRegion A pointer to the region @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_SUCCESS The region was returned properly @retval EFI_INVALID_PARAMETER pRegion is NULL @retval EFI_NO_RESPONSE FW busy on one or more PMem modules **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_REGION) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 RegionId, OUT struct _REGION_INFO *pRegion, OUT COMMAND_STATUS *pCommandStatus ); /** Gather info about total capacities on all PMem modules @param[in] pThis a pointer to EFI_DCPMM_CONFIG2_PROTOCOL instance @param[out] pMemoryResourcesInfo structure filled with required information @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_ABORTED PCAT tables not found @retval Other errors failure of FW commands @retval EFI_SUCCESS Success **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_MEMORY_RESOURCES_INFO) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT MEMORY_RESOURCES_INFO *pMemoryResourcesInfo ); /** Gather info about performance on all PMem modules @param[in] pThis a pointer to EFI_DCPMM_CONFIG2_PROTOCOL instance @param[out] pDimmCount pointer to the number of PMem modules on list @param[out] pDimmsPerformanceData list of PMem modules' performance data @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_ABORTED PCAT tables not found @retval Other errors failure of FW commands @retval EFI_SUCCESS Success **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_DIMMS_PERFORMANCE) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pDimmCount, OUT DIMM_PERFORMANCE_DATA **pDimmsPerformanceData ); /** Get System Capabilities information from PCAT tables Pointer to variable length pInterleaveFormatsSupported is allocated here and must be freed by caller. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[out] pSysCapInfo is a pointer to table with System Capabilities information @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER NULL argument @retval EFI_NOT_STARTED Pcat tables not parsed **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_SYSTEM_CAPABILITIES_INFO) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT SYSTEM_CAPABILITIES_INFO *pSysCapInfo ); /** Update firmware or training data in one or all PMem modules of the system @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds is a pointer to an array of PMem module IDs - if NULL, execute operation on all PMem modules @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] pFileName Name is a pointer to a file containing FW image @param[in] pWorkingDirectory is a pointer to a path to FW image file @param[in] Examine flag enables image verification only @param[in] Force flag suppresses warning message in case of attempted downgrade @param[in] Recovery **Deprecated** Run the update on non-functional PMem modules only @param[in] Reserved Set to FALSE @param[out] pFwImageInfo is a pointer to a structure containing FW image information need to be provided if examine flag is set @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER One of parameters provided is not acceptable @retval EFI_NOT_FOUND there is no PMem module with such Pid @retval EFI_OUT_OF_RESOURCES Unable to allocate memory for a data structure @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_SUCCESS Update has completed successfully **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_UPDATE_FW) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN CHAR16 *pFileName, IN CHAR16 *pWorkingDirectory OPTIONAL, IN BOOLEAN Examine, IN BOOLEAN Force, IN BOOLEAN Recovery, IN BOOLEAN Reserved, OUT NVM_FW_IMAGE_INFO *pFwImageInfo OPTIONAL, OUT COMMAND_STATUS *pCommandStatus ); /** Get PMem module alarm thresholds @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmPid The ID of the PMem module @param[in] SensorId Sensor id to retrieve information for @param[out] pNonCriticalThreshold Current non-critical threshold for sensor @param[out] pEnabledState Current enable state for sensor @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER if no PMem module found for DimmPid or input parameter is NULL. @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_DEVICE_ERROR device error detected @retval EFI_SUCCESS Success **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_ALARM_THR) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmPid, IN UINT8 SensorId, OUT INT16 *pNonCriticalThreshold, OUT UINT8 *pEnabledState, OUT COMMAND_STATUS *pCommandStatus ); /** Set PMem module alarm thresholds @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] SensorId Sensor id to set values for @param[in] NonCriticalThreshold New non-critical threshold for sensor @param[in] EnabledState New enable state for sensor @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All Ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_SET_ALARM_THR) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, IN UINT8 SensorId, IN INT16 NonCriticalThreshold, IN UINT8 EnabledState, OUT COMMAND_STATUS *pCommandStatus ); /** Get PMem module Health Info This FW command is used to retrieve current health of system, including SMART information: * Overall health status * Temperature * Spare blocks * Alarm Trips set (Temperature/Spare Blocks) * Device life span as a percentage * Last shutdown status * Dirty shutdowns * Last shutdown time. * Power Cycles (does not include warm resets or S3 resumes) * Power on time (life of PMem module has been powered on) * Uptime for current power cycle in seconds @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmPid The ID of the PMem module @param[out] pHealthInfo - pointer to structure containing all Health and Smart variables @retval EFI_INVALID_PARAMETER if no PMem module found for DimmPid. @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_DEVICE_ERROR device error detected @retval EFI_SUCCESS Success **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_SMART_AND_HEALTH) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmPid, OUT SMART_AND_HEALTH_INFO *pHealthInfo ); /** Get actual Region goal capacities that would be used based on input values. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[in] PersistentMemType Persistent memory type @param[in, out] pVolatilePercent Volatile region size in percents. @param[in] ReservedPercent Amount of AppDirect memory to not map in percents @param[in] ReserveDimm Reserve one PMem module for use as a not interleaved AppDirect memory @param[out] pConfigGoals pointer to output array @param[out] pConfigGoalsCount number of elements written @param[out] pNumOfDimmsTargeted number of PMem modules targeted in a goal config request @param[out] pMaxPMInterleaveSetsPerDie pointer to Maximum PM Interleave Sets per Die @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_NO_RESPONSE FW busy for one or more PMem modules @retval EFI_SUCCESS All Ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_ACTUAL_REGIONS_GOAL_CAPACITIES) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds OPTIONAL, IN UINT32 SocketIdsCount, IN UINT8 PersistentMemType, IN OUT UINT32 *pVolatilePercent, IN UINT32 ReservedPercent, IN UINT8 ReserveDimm, OUT REGION_GOAL_PER_DIMM_INFO *pConfigGoals, OUT UINT32 *pConfigGoalsCount, OUT UINT32 *pNumOfDimmsTargeted OPTIONAL, OUT UINT32 *pMaxPMInterleaveSetsPerDie OPTIONAL, OUT COMMAND_STATUS *pCommandStatus ); /** Create region goal configuration @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Examine Do a dry run if set @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[in] PersistentMemType Persistent memory type @param[in] VolatilePercent Volatile region size in percents @param[in] ReservedPercent Amount of AppDirect memory to not map in percents @param[in] ReserveDimm Reserve one PMem module for use as a not interleaved AppDirect memory @param[in] LabelVersionMajor Major version of label to init @param[in] LabelVersionMinor Minor version of label to init @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All Ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_CREATE_GOAL) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN BOOLEAN Examine, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds OPTIONAL, IN UINT32 SocketIdsCount, IN UINT8 PersistentMemType, IN UINT32 VolatilePercent, IN UINT32 ReservedPercent, IN UINT8 ReserveDimm, IN UINT16 LabelVersionMajor, IN UINT16 LabelVersionMinor, OUT UINT32 *pMaxPMInterleaveSetsPerDie OPTIONAL, OUT COMMAND_STATUS *pCommandStatus ); /** Delete region goal configuration @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid EFI_DCPMM_CONFIG_DELETE_GOAL DeleteGoalConfig; @retval EFI_SUCCESS All Ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_DELETE_GOAL) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds OPTIONAL, IN UINT32 SocketIdsCount, OUT COMMAND_STATUS *pCommandStatus ); /** Get region goal configuration @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[out] pConfigGoals pointer to output array @param[out] pConfigGoalsCount number of elements written @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_NO_RESPONSE FW busy for one or more PMem modules @retval EFI_SUCCESS All Ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_GOALS) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds OPTIONAL, IN UINT32 SocketIdsCount, IN CONST UINT32 ConfigGoalTableSize, OUT REGION_GOAL_PER_DIMM_INFO *pConfigGoals, OUT UINT32 *pConfigGoalsCount, OUT COMMAND_STATUS *pCommandStatus ); /** Dump region goal configuration into the file @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pFilePath Name is a pointer to a dump file path @param[in] pDevicePath is a pointer to a device where dump file will be stored @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_NO_RESPONSE FW busy for one or more PMem modules @retval EFI_SUCCESS All Ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_DUMP_GOAL) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN CHAR16 *pFilePath, IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath, OUT COMMAND_STATUS *pCommandStatus ); /** Load region goal configuration from file @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[in] pFileString Buffer for Region Goal configuration from file @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All Ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_LOAD_GOAL) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds, IN UINT32 SocketIdsCount, IN CHAR8 *pFileString, OUT COMMAND_STATUS *pCommandStatus ); typedef struct DIAGNOSTIC_INFO { CHAR16 *TestName; CHAR16 *Message; CHAR16 *State; UINT8 StateVal; UINT32 ResultCode; CHAR16 *SubTestName[MAX_NO_OF_DIAGNOSTIC_SUBTESTS]; UINT8 SubTestStateVal[MAX_NO_OF_DIAGNOSTIC_SUBTESTS]; CHAR16 *SubTestState[MAX_NO_OF_DIAGNOSTIC_SUBTESTS]; CHAR16 *SubTestMessage[MAX_NO_OF_DIAGNOSTIC_SUBTESTS]; CHAR16 *SubTestEventCode[MAX_NO_OF_DIAGNOSTIC_SUBTESTS]; } DIAG_INFO; /** Start Diagnostic Detail @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] DiagnosticTestId ID of a diagnostic test to be started @param[in] DimmIdPreference Preference for the PMem module ID (handle or UID) @param[out] ppResult Pointer to structure that holds results of the tests @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_NOT_STARTED Test was not executed @retval EFI_SUCCESS All Ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_START_DIAGNOSTIC) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN CONST UINT8 DiagnosticTestId, IN UINT8 DimmIdPreference, OUT DIAG_INFO **ppResultStr ); /** Create namespace Creates a AppDirect namespace on the provided region/PMem module. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] RegionId the ID of the region that the Namespace is supposed to be created. @param[in] Reserved @param[in] BlockSize the size of each of the block in the device. Valid block sizes are: 1 (for AppDirect Namespace), 512 (default), 514, 520, 528, 4096, 4112, 4160, 4224. @param[in] BlockCount the amount of block that this namespace should consist @param[in] pName - Namespace name. @param[in] Mode- boolean value to decide when the namespace should have the BTT arena included * 0 - Ignore * 1 - Yes * 2 - No @param[in] ForceAll Suppress all warnings @param[in] ForceAlignment Suppress alignment warnings @param[out] pActualNamespaceCapacity capacity needed to meet alignment requirements @param[out] pNamespaceId Pointer to the ID of the namespace that is created @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS if the operation was successful. @retval EFI_ALREADY_EXISTS if a namespace with the provided GUID already exists in the system. @retval EFI_DEVICE_ERROR if there was a problem with writing the configuration to the device. @retval EFI_OUT_OF_RESOURCES if there is not enough free space on the PMem module/region. @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_CREATE_NAMESPACE) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 RegionId, IN UINT16 Reserved, IN UINT32 BlockSize, IN UINT64 BlockCount, IN CHAR8 *pName, IN BOOLEAN Mode, IN BOOLEAN ForceAll, IN BOOLEAN ForceAlignment, OUT UINT64 *pActualNamespaceCapacity, OUT UINT16 *pNamespaceId, OUT COMMAND_STATUS *pCommandStatus ); /** Delete Namespace @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] Force Force to perform deleting namespace configs on all affected PMem modules @param[in] NamespaceId the ID of the namespace to be removed. @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS if the operation was successful. @retval EFI_NOT_FOUND if a namespace with the provided GUID does not exist in the system. @retval EFI_DEVICE_ERROR if there was a problem with writing the configuration to the device. @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_DELETE_NAMESPACE) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN BOOLEAN Force, IN UINT16 NamespaceId, OUT COMMAND_STATUS *pCommandStatus ); /** Get Driver API Version @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pVersion output version @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_GET_DRIVER_API_VERSION) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT CHAR16 pVersion[FW_API_VERSION_LEN] ); /** Get namespaces info @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pNamespaceListNode - pointer to namespace list node @param[out] pNamespacesCount - namespace count @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval EFI_NO_RESPONSE FW busy for one or more PMem modules @retval EFI_SUCCESS All ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_NAMESPACES) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN OUT LIST_ENTRY *pNamespaceListNode, OUT UINT32 *pNamespacesCount, OUT COMMAND_STATUS *pCommandStatus ); /** Get Error log for given PMem module @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds - array of PMem module pids. Use all PMem modules if pDimms is NULL and DimmsCount is 0. @param[in] DimmsCount - number of PMem modules in array. Use all PMem modules if pDimms is NULL and DimmsCount is 0. @param[in] ThermalError - is thermal error (if not it is media error) @param[in] SequenceNumber - sequence number of error to fetch in queue @param[in] HighLevel - high level if true, low level otherwise @param[in, out] pCount - number of error entries in output array @param[out] pErrorLogs - output array of errors @param[out] pCommandStatus Structure containing detailed NVM error codes. @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_ERROR_LOG) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN CONST UINT32 DimmsCount, IN CONST BOOLEAN ThermalError, IN CONST UINT16 SequenceNumber, IN CONST BOOLEAN HighLevel, IN OUT UINT32 *pMaxErrorsToFetch, OUT ERROR_LOG_INFO *pErrorLogs, OUT COMMAND_STATUS *pCommandStatus ); /** Get the debug log from a specified PMem module and fw debug log source @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmID identifier of what PMem module to get log pages from @param[in] LogSource debug log source buffer to retrieve @param[in] Reserved for future use. Must be 0 for now. @param[out] ppDebugLogBuffer - an allocated buffer containing the raw debug log @param[out] pDebugLogBufferSize - the size of the raw debug log buffer @param[out] pCommandStatus structure containing detailed NVM error codes Note: The caller is responsible for freeing the returned buffer @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_FW_DEBUG_LOG) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, IN UINT8 LogSource, IN UINT32 Reserved, OUT VOID **ppDebugLogBuffer, OUT UINTN *pDebugLogBufferSize, OUT COMMAND_STATUS *pCommandStatus ); /** Set Optional Configuration Data Policy using FW command @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds - pointer to array of UINT16 PMem module ids to set @param[in] DimmIdsCount - number of elements in pDimmIds @param[in] Reserved @param[in] AveragePowerReportingTimeConstant - (FIS 2.1 and greater) AveragePowerReportingTimeConstant value to set @param[out] pCommandStatus Structure containing detailed NVM error codes. @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok @retval EFI_NO_RESPONSE FW busy for one or more dimms **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_SET_OPTIONAL_DATA_POLICY) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT8 *Reserved, IN UINT32 *pAveragePowerReportingTimeConstant, OUT COMMAND_STATUS *pCommandStatus ); /** Get requested number of specific PMem module registers for given PMem module id @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmId - ID of a PMem module. @param[out] pBsr - Pointer to buffer for Boot Status register, contains high and low 4B register. @param[out] Reserved @param[out] pCommandStatus Structure containing detailed NVM error codes. @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_RETRIEVE_DIMM_REGISTERS) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmId, OUT UINT64 *pBsr, OUT UINT8 *Reserved, OUT COMMAND_STATUS *pCommandStatus ); /** Get system topology from SMBIOS table @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] ppTopologyDimm Structure containing information about DDR entries from SMBIOS. @param[out] pTopologyDimmsNumber Number of DDR entries found in SMBIOS. @retval EFI_SUCCESS All ok. @retval EFI_DEVICE_ERROR Unable to find SMBIOS table in system configuration tables. @retval EFI_OUT_OF_RESOURCES Problem with allocating memory **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_SYSTEM_TOPOLOGY) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT TOPOLOGY_DIMM_INFO **ppTopologyDimm, OUT UINT16 *pTopologyDimmsNumber ); /** Get the system-wide ARS status for the persistent memory capacity of the system. In this function, the system-wide ARS status is determined based on the ARS status values for the individual PMem modules. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pARSStatus pointer to the current system ARS status. @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_ARS_STATUS) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT8 *pARSStatus ); /** Get the User Driver Preferences. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pDriverPreferences pointer to the current driver preferences. @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_DRIVER_PREFERENCES) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT DRIVER_PREFERENCES *pDriverPreferences, OUT COMMAND_STATUS *pCommandStatus ); /** Set the User Driver Preferences. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDriverPreferences pointer to the desired driver preferences. @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_SET_DRIVER_PREFERENCES) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN DRIVER_PREFERENCES *pDriverPreferences, OUT COMMAND_STATUS *pCommandStatus ); /** Attempt to format a PMem module through a customer format command @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] Recovery - Perform on non-functional PMem modules @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_OUT_OF_RESOURCES Memory Allocation failed @retval EFI_SUCCESS All Ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_DIMM_FORMAT) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, IN BOOLEAN Recovery, OUT COMMAND_STATUS *pCommandStatus ); /** Get DDRT IO init info @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] DimmID DimmID of device to retrieve support data from @param[out] pDdrtTrainingStatus pointer to the PMem modules DDRT training status @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_DDRT_IO_INIT_INFO) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT UINT8 *pDdrtTrainingStatus ); /** Get long operation status @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] DimmID DimmID of device to retrieve status from @param[in] pOpcode pointer to opcode of long op command to check @param[in] pOpcode pointer to subopcode of long op command to check @param[out] pPercentComplete pointer to percentage current command has completed @param[out] pEstimatedTimeLeft pointer to time to completion BCD @param[out] pFwStatus pointer to completed mailbox status code @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_LONG_OP_STATUS) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT UINT8 *pOpcode OPTIONAL, OUT UINT8 *pSubOpcode OPTIONAL, OUT UINT16 *pPercentComplete OPTIONAL, OUT UINT32 *pEstimatedTimeLeft OPTIONAL, OUT EFI_STATUS *pFwStatus ); /** InjectError @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds - pointer to array of UINT16 PMem module ids to get data for @param[in] DimmIdsCount - number of elements in pDimmIds @param[IN] ErrorInjType - Error Inject type @param[IN] ClearStatus - Is clear status set @param[IN] pInjectTemperatureValue - Pointer to inject temperature @param[IN] pInjectPoisonAddress - Pointer to inject poison address @param[IN] pPoisonType - Pointer to poison type @param[IN] pPercentRemaining - Pointer to percentage remaining @param[out] pCommandStatus Structure containing detailed NVM error codes. @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_INJECT_ERROR) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT8 ErrorInjType, IN UINT8 ClearStatus, IN UINT64 *pInjectTemperatureValue, IN UINT64 *pInjectPoisonAddress, IN UINT8 *pPoisonType, IN UINT8 *pPercentageremaining, OUT COMMAND_STATUS *pCommandStatus ); /** GetBsr value and return bsr or boot status bitmask depending on the requested options UEFI - Read directly from BSR register OS - Get BSR value from BIOS emulated command @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmID PMem module handle of the PMem module @param[out] pBsrValue pointer to BSR register value OPTIONAL @param[out] pBootStatusBitMask pointer to BootStatusBitmask OPTIONAL @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_NO_RESPONSE BSR value returned by FW is invalid @retval EFI_SUCCESS Success @retval Other errors failure of FW commands **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_BSR_AND_BITMASK) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT UINT64 *pBsrValue OPTIONAL, OUT UINT16 *pBootStatusBitmask OPTIONAL ); /** Get Command Access Policy is used to retrieve a list of FW commands that may be restricted. @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmID Handle of the PMem module @param[in,out] pCount IN: Count is number of elements in the pCapInfo array. OUT: number of elements written to pCapInfo @param[out] pCapInfo Array of Command Access Policy Entries. If NULL, pCount will be updated with number of elements required. OPTIONAL @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_SUCCESS Success @retval Other errors failure of FW commands **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_COMMAND_ACCESS_POLICY) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, IN OUT UINT32 *pCount, IN OUT COMMAND_ACCESS_POLICY_ENTRY *pCapInfo OPTIONAL ); /** Get Command Effect Log is used to retrieve a list PMem module FW commands and their effects on the PMem module subsystem. @param[in] pThis - A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmID - Handle of the PMem module @param[in, out] pCelEntry - A pointer to the CEL entry table for a given PMem module @param[in, out] EntryCount - The number of CEL entries for a given table @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_COMMAND_EFFECT_LOG) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, IN OUT COMMAND_EFFECT_LOG_ENTRY **ppLogEntry, IN OUT UINT32 *pEntryCount ); /** Pass Through command to FW Sends a command to FW and waits for response from firmware @param[in,out] pCmd A firmware command structure @param[in] Timeout The timeout, in 100ns units, to use for the execution of the protocol command. A Timeout value of 0 means that this function will wait indefinitely for the protocol command to execute. If Timeout is greater than zero, then this function will return EFI_TIMEOUT if the time required to execute the receive data command is greater than Timeout. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid FW Command Parameter. @retval EFI_NOT_FOUND The driver could not find the encoded in pCmd PMem module in the system. @retval EFI_DEVICE_ERROR FW error received. @retval EFI_UNSUPPORTED if the command is ran not in the DEBUG version of the driver. @retval EFI_TIMEOUT A timeout occurred while waiting for the protocol command to execute. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_PASS_THRU) ( IN OUT NVM_FW_CMD *pCmd, IN UINT64 Timeout ); /** Gets value of transport protocol and payload size settings from platform @param[in] pThis A pointer to EFI DCPMM CONFIG PROTOCOL structure @param[in,out] pAttribs A pointer to a variable used to store protocol and payload settings @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid FW Command Parameter. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_GET_FIS_TRANSPORT_ATTRIBS) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS *pAttribs ); /** Sets value of transport protocol and payload size settings for platform @param[in] pThis A pointer to EFI DCPMM CONFIG PROTOCOL structure @param[in] Attribs The new value to assign to protocol and payload settings @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid FW Command Parameter. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_CONFIG_SET_FIS_TRANSPORT_ATTRIBS) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS Attribs ); /** Sets the playback/recording mode @param[in] PbrMode: 0x0 - Normal, 0x1 - Recording, 0x2 - Playback @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_NOT_READY if PbrMode is set to Playback and session isn't loaded **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_SET_MODE) ( IN UINT32 PbrMode ); /** Gets the current playback/recording mode @param[out] pPbrMode: 0x0 - Normal, 0x1 - Recording, 0x2 - Playback @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_NOT_READY if the pbr context is not available @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_GET_MODE) ( OUT UINT32 *pPbrMode ); /** Set the PBR Buffer to use @param[in] pBufferAddress: address of a buffer to use for playback or record mode @param[in] BufferSize: size in bytes of the buffer @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_NOT_READY if the pbr context is not available **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_SET_SESSION) ( IN VOID *pBufferAddress, IN UINT32 BufferSize ); /** Get the PBR Buffer that is current being used @param[out] ppBufferAddress: address to the pbr buffer @param[out] pBufferSize: size in bytes of the buffer @retval EFI_SUCCESS if the table was found and is properly returned. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_GET_SESSION) ( IN VOID **ppBufferAddress, IN UINT32 *pBufferSize ); /** Clear the PBR Buffer that is current being used @retval EFI_SUCCESS if the table was found and is properly returned. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_FREE_SESSION) ( ); /** Reset all playback buffers to align with the specified TagId @retval EFI_SUCCESS if the table was found and is properly returned. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_RESET_SESSION) ( IN UINT32 TagId ); /** Set a tag associated with the current recording buffer offset @param[in] Signature: signature associated with the tag @param[in] pName: name associated with the tag @param[in] pDescription: description of the tab @param[out] pId: logical ID given to the tag (will be appended to the name) @retval EFI_SUCCESS if the table was found and is properly returned. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_SET_TAG) ( IN UINT32 Signature, IN CHAR16 *pName, IN CHAR16 *pDescription, OUT UINT32 *pId ); /** Get the number of tags associated with the recording buffer @param[out] pCount: get the number of tags @retval EFI_SUCCESS if the table was found and is properly returned. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_GET_TAG_COUNT) ( OUT UINT32 *pCount ); /** Gets information pertaining to playback data associated with a specific data type (Signature). @param[in] Signature: Specifies data type interested in @param[out] pTotalDataItems: Number of logical data items of this Signature type that are available in the currently active playback session. @param[out] pTotalDataSize: Total size in bytes of all data associated with Signature type @param[out] pCurrentPlaybackDataOffset: Points to the next data item of Signature type that will be returned when PbrGetData is called with GET_NEXT_DATA_INDEX. @retval EFI_SUCCESS on success **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_GET_PB_INFO) ( IN UINT32 Signature, OUT UINT32 *pTotalDataItems, OUT UINT32 *pTotalDataSize, OUT UINT32 *pCurrentPlaybackDataOffset ); /** Adds data to the recording session @param[in] Signature: unique dword identifier that categorizes the data to be recorded @param[in] pData: Data to be recorded. If NULL, a zeroed data buffer is allocated. Useful, when used with ppData. @param[in] Size: Byte size of pData @param[in] Singleton: Only one data object associated with Signature. Data previously set will be overridden with this data object. @param[out] - ppData - May be NULL, otherwise will contain a pointer to the memory allocated in the recording buffer for this data object. Warning, this pointer is only guaranteed to be valid until the next call to this function. @retval EFI_SUCCESS on success **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_SET_DATA) ( IN UINT32 Signature, IN VOID *pData, IN UINT32 Size, IN BOOLEAN Singleton, OUT VOID **ppData, OUT UINT32 *pLogicalIndex ); /** Gets data from the playback session @param[in] Signature: Specifies which data type to get @param[in] Index: GET_NEXT_DATA_INDEX gets the next data object within the playback session. Otherwise, any positive value will result in getting the data object at position 'Index' (base 0). If data associated with Signature is a Singleton, use Index '0'. @param[out] ppData: Newly allocated buffer that contains the data object. Caller is responsible for freeing it. @param[out] pSize: Size in bytes of ppData. @param[out] pLogicalIndex: May be NULL, otherwise will contain the logical index of the data object. @retval EFI_SUCCESS on success **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_GET_DATA) ( IN UINT32 Signature, IN INT32 Index, OUT VOID **ppData, OUT UINT32 *pSize, OUT UINT32 *pLogicalIndex ); /** Get tag info @param[in] pId: tag identification @param[out] pSignature: signature associated with the tag @param[out] pName: name associated with the tag @param[out] ppDescription: description of the tab @param[out] ppTagPartitionInfo: array of TagPartitionInfo structs @param[out] pTagPartitionCnt: number of items in pTagPartitionInfo All out pointers need to be freed by caller. @retval EFI_SUCCESS if the table was found and is properly returned. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_GET_TAG) ( IN UINT32 Id, OUT UINT32 *pSignature, OUT CHAR16 **ppName, OUT CHAR16 **ppDescription, OUT VOID **ppTagPartitionInfo, OUT UINT32 *pTagPartitionCnt ); #ifndef OS_BUILD /** Gets value of PcdDebugPrintErrorLevel for the pmem driver @param[in] pThis A pointer to EFI DCPMM CONFIG PROTOCOL structure @param[out] ErrorLevel A pointer used to store the value of debug print error level @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid ErrorLevel Parameter. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_GET_DRIVER_DEBUG_PRINT_ERROR_LEVEL) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *ErrorLevel ); /** Sets value of PcdDebugPrintErrorLevel for the pmem driver @param[in] pThis A pointer to EFI DCPMM CONFIG PROTOCOL structure @param[in] ErrorLevel The new value to assign to debug print error level @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid ErrorLevel Parameter. **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_PBR_SET_DRIVER_DEBUG_PRINT_ERROR_LEVEL) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT32 ErrorLevel ); #endif //OS_BUILD /** Get FIPS Mode retrieves the current FIPS Mode for the DimmID provided. @param[in] pThis - A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] DimmID - Handle of the PMem module @param[out] pFIPSMode - A pointer to a FIPS_MODE struct to fill in @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ typedef EFI_STATUS (EFIAPI *EFI_DCPMM_GET_FIPS_MODE) ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT FIPS_MODE *pFIPSMode, OUT COMMAND_STATUS *pCommandStatus ); /** Configuration and management of PMem modules Protocol Interface **/ struct _EFI_DCPMM_CONFIG2_PROTOCOL { UINT32 Version; EFI_DCPMM_CONFIG_GET_DIMM_COUNT GetDimmCount; EFI_DCPMM_CONFIG_GET_DIMMS GetDimms; EFI_DCPMM_CONFIG_GET_DIMM GetDimm; #ifdef OS_BUILD EFI_DCPMM_CONFIG_GET_PMON GetPMONRegisters; EFI_DCPMM_CONFIG_SET_PMON SetPMONRegisters; #endif EFI_DCPMM_CONFIG_GET_UNINITIALIZED_DIMM_COUNT GetUninitializedDimmCount; EFI_DCPMM_CONFIG_GET_UNINITIALIZED_DIMMS GetUninitializedDimms; EFI_DCPMM_CONFIG_GET_SOCKETS GetSockets; EFI_DCPMM_CONFIG_GET_DIMM_SMBIOS_TABLE GetDimmSmbiosTable; EFI_DCPMM_CONFIG_GET_ACPI_NFIT GetAcpiNFit; EFI_DCPMM_CONFIG_GET_ACPI_PCAT GetAcpiPcat; EFI_DCPMM_CONFIG_GET_ACPI_PMTT GetAcpiPMTT; EFI_DCPMM_CONFIG_GET_PCD GetPcd; EFI_DCPMM_CONFIG_GET_SECURITY_STATE GetSecurityState; EFI_DCPMM_CONFIG_SET_SECURITY_STATE SetSecurityState; EFI_DCPMM_CONFIG_UPDATE_FW UpdateFw; EFI_DCPMM_CONFIG_GET_REGION_COUNT GetRegionCount; EFI_DCPMM_CONFIG_GET_REGIONS GetRegions; EFI_DCPMM_CONFIG_GET_REGION GetRegion; EFI_DCPMM_CONFIG_GET_MEMORY_RESOURCES_INFO GetMemoryResourcesInfo; EFI_DCPMM_CONFIG_GET_DIMMS_PERFORMANCE GetDimmsPerformanceData; EFI_DCPMM_CONFIG_GET_SYSTEM_CAPABILITIES_INFO GetSystemCapabilitiesInfo; EFI_DCPMM_GET_DRIVER_API_VERSION GetDriverApiVersion; EFI_DCPMM_CONFIG_GET_ALARM_THR GetAlarmThresholds; EFI_DCPMM_CONFIG_SET_ALARM_THR SetAlarmThresholds; EFI_DCPMM_CONFIG_GET_SMART_AND_HEALTH GetSmartAndHealth; EFI_DCPMM_CONFIG_GET_GOALS GetGoalConfigs; EFI_DCPMM_CONFIG_GET_ACTUAL_REGIONS_GOAL_CAPACITIES GetActualRegionsGoalCapacities; EFI_DCPMM_CONFIG_CREATE_GOAL CreateGoalConfig; EFI_DCPMM_CONFIG_DELETE_GOAL DeleteGoalConfig; EFI_DCPMM_CONFIG_DUMP_GOAL DumpGoalConfig; EFI_DCPMM_CONFIG_LOAD_GOAL LoadGoalConfig; EFI_DCPMM_CONFIG_START_DIAGNOSTIC StartDiagnostic; EFI_DCPMM_CONFIG_CREATE_NAMESPACE CreateNamespace; EFI_DCPMM_CONFIG_GET_NAMESPACES GetNamespaces; EFI_DCPMM_CONFIG_DELETE_NAMESPACE DeleteNamespace; EFI_DCPMM_CONFIG_GET_ERROR_LOG GetErrorLog; EFI_DCPMM_CONFIG_GET_FW_DEBUG_LOG GetFwDebugLog; EFI_DCPMM_CONFIG_SET_OPTIONAL_DATA_POLICY SetOptionalConfigurationDataPolicy; EFI_DCPMM_CONFIG_RETRIEVE_DIMM_REGISTERS RetrieveDimmRegisters; EFI_DCPMM_CONFIG_GET_SYSTEM_TOPOLOGY GetSystemTopology; EFI_DCPMM_CONFIG_GET_ARS_STATUS GetARSStatus; EFI_DCPMM_CONFIG_GET_DRIVER_PREFERENCES GetDriverPreferences; EFI_DCPMM_CONFIG_SET_DRIVER_PREFERENCES SetDriverPreferences; EFI_DCPMM_CONFIG_DIMM_FORMAT DimmFormat; EFI_DCPMM_CONFIG_GET_DDRT_IO_INIT_INFO GetDdrtIoInitInfo; EFI_DCPMM_CONFIG_GET_LONG_OP_STATUS GetLongOpStatus; EFI_DCPMM_CONFIG_INJECT_ERROR InjectError; EFI_DCPMM_CONFIG_GET_BSR_AND_BITMASK GetBSRAndBootStatusBitMask; EFI_DCPMM_CONFIG_PASS_THRU PassThru; EFI_DCPMM_CONFIG_DELETE_PCD_CONFIG ModifyPcdConfig; EFI_DCPMM_CONFIG_GET_FIS_TRANSPORT_ATTRIBS GetFisTransportAttributes; EFI_DCPMM_CONFIG_SET_FIS_TRANSPORT_ATTRIBS SetFisTransportAttributes; EFI_DCPMM_CONFIG_GET_COMMAND_ACCESS_POLICY GetCommandAccessPolicy; EFI_DCPMM_CONFIG_GET_COMMAND_EFFECT_LOG GetCommandEffectLog; #ifndef OS_BUILD EFI_DCPMM_PBR_GET_DRIVER_DEBUG_PRINT_ERROR_LEVEL GetDriverDebugPrintErrorLevel; EFI_DCPMM_PBR_SET_DRIVER_DEBUG_PRINT_ERROR_LEVEL SetDriverDebugPrintErrorLevel; #endif //OS_BUILD EFI_DCPMM_GET_FIPS_MODE GetFIPSMode; }; /** Playback and Record APIs **/ struct _EFI_DCPMM_PBR_PROTOCOL { EFI_DCPMM_PBR_SET_MODE PbrSetMode; EFI_DCPMM_PBR_GET_MODE PbrGetMode; EFI_DCPMM_PBR_SET_SESSION PbrSetSession; EFI_DCPMM_PBR_GET_SESSION PbrGetSession; EFI_DCPMM_PBR_FREE_SESSION PbrFreeSession; EFI_DCPMM_PBR_RESET_SESSION PbrResetSession; EFI_DCPMM_PBR_SET_TAG PbrSetTag; EFI_DCPMM_PBR_GET_TAG_COUNT PbrGetTagCount; EFI_DCPMM_PBR_GET_TAG PbrGetTag; EFI_DCPMM_PBR_GET_PB_INFO PbrGetDataPlaybackInfo; EFI_DCPMM_PBR_GET_DATA PbrGetData; EFI_DCPMM_PBR_SET_DATA PbrSetData; }; #endif /** _NVM_INTERFACE_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/NvmLimits.h000066400000000000000000000045671440615110200205370ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _NVM_LIMITS_H_ #define _NVM_LIMITS_H_ #define MAX_SOCKETS 16 #define MAX_IMCS_PER_SOCKET 4 #define MAX_CHANNELS_PER_IMC 3 // 3 is for backwards compatibility #define MAX_DIMMS_PER_CHANNEL 2 #define MAX_DIMMS_PER_IMC (MAX_CHANNELS_PER_IMC * MAX_DIMMS_PER_CHANNEL) // 2 * 3 = 6 #define MAX_DIMMS_PER_SOCKET (MAX_DIMMS_PER_IMC * MAX_IMCS_PER_SOCKET) // 4 * 4 = 16 // Die ID will always be zero for single die CPU #define MAX_DIEID_SINGLE_DIE_SOCKET 0 /** MAX_DIMMS = MAX_SOCKETS * MAX_DIMMS_PER_SOCKET, but we have to use a pure number in this case, because there is a compilation issue while HII is generating (HII uses MAX_DIMMS) **/ #define MAX_DIMMS 128 #define MAX_IS_PER_DIMM 2 #define MAX_IS_PER_SOCKET (MAX_DIMMS_PER_SOCKET * MAX_IS_PER_DIMM) // 16 * 2 = 32 /** MAX_IS_CONFIGS = MAX_DIMMS * MAX_IS_PER_DIMM, but we have to use a pure number in this case, because there is a compilation issue while HII is generating **/ #define MAX_IS_CONFIGS 256 // 128 * 2 /** MAX_REGIONS = MAX_SOCKETS * MAX_REGIONS_PER_SOCKET, but we have to use a pure number in this case, because there is a compilation issue while HII is generating **/ #define MAX_REGIONS_PER_SOCKET 16 #define MAX_REGIONS 44 /** Cache line size in bytes. Used by all of the platforms supporting Intel DCPMMs **/ #define CACHE_LINE_SIZE 64 /** Label Storage Area structure information **/ #define NAMESPACE_INDEXES 2 #define MAX_NAMESPACE_RANGES 64 #define NAMESPACE_LABEL_INDEX_ALIGN 256 #define MIN_NAMESPACE_LABEL_INDEX_SIZE 256 #define TEMPERATURE_THRESHOLD_MIN 0 #define TEMPERATURE_CONTROLLER_THRESHOLD_MAX 102 //!< max is defined in FIS #define TEMPERATURE_MEDIA_THRESHOLD_MAX 85 //!< max is defined in FIS #define TEMPERATURE_MEDIA_THRESHOLD_DEFAULT 82 #define TEMPERATURE_CONTROLLER_THRESHOLD_DEFAULT 98 #define CAPACITY_THRESHOLD_MIN 1 #define CAPACITY_THRESHOLD_MAX 99 #define CAPACITY_THRESHOLD_DEFAULT 50 #define ENABLED_STATE_DISABLE 0 #define ENABLED_STATE_ENABLE 1 #define PASSPHRASE_BUFFER_SIZE 32 //!< Length of a passphrase buffer #define MAX_OVERWRITE_PASS_COUNT 15 //!< Overwrite DIMM max pass count #define MAX_IFC_NUM 2 #define MAX_APPDIRECT_SETTINGS_SUPPORTED 25 #endif /** _NVM_LIMITS_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/NvmSharedDefs.h000066400000000000000000000520361440615110200213000ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file NvmSharedDefs.h * @brief Definition of the Non-Volatile Memory (NVM) status codes. */ #ifndef _NVM_SHARED_DEFS_H_ #define _NVM_SHARED_DEFS_H_ /** * @brief NVM_API return codes */ typedef enum _NvmStatusCode { NVM_SUCCESS = 0, ///< Success NVM_SUCCESS_FW_RESET_REQUIRED = 1, ///< Success, but FW reset required NVM_ERR_OPERATION_NOT_STARTED = 2, ///< Error: Operation not started NVM_ERR_OPERATION_FAILED = 3, ///< Error: Operation failed NVM_ERR_FORCE_REQUIRED = 4, ///< Error: Force parameter required NVM_ERR_INVALID_PARAMETER = 5, ///< Error: Invalid parameter NVM_ERR_COMMAND_NOT_SUPPORTED_BY_THIS_SKU = 9, ///< Error: Command not supported by this SKU NVM_ERR_DIMM_NOT_FOUND = 11, ///< Error: PMem module not found NVM_ERR_DIMM_ID_DUPLICATED = 12, ///< Error: PMem module ID duplicated NVM_ERR_SOCKET_ID_NOT_VALID = 13, ///< Error: Socket ID not valid NVM_ERR_SOCKET_ID_INCOMPATIBLE_W_DIMM_ID = 14, ///< Error: Socket ID incompatible with PMem module ID NVM_ERR_SOCKET_ID_DUPLICATED = 15, ///< Error: Socket ID duplicated NVM_ERR_CONFIG_NOT_SUPPORTED_BY_CURRENT_SKU = 16, ///< Error: Config Not supported by current SKU NVM_ERR_MANAGEABLE_DIMM_NOT_FOUND = 17, ///< Error: Manageable PMem module not found NVM_ERR_NO_USABLE_DIMMS = 18, ///< Error: No usable PMem modules due to all PMem modules being unmanageable, non-functional, or having a population issue NVM_ERR_DIMM_EXCLUDED = 19, ///< Error: PMem module excluded as it is unmanageable, non-functional, or has a population issue. NVM_ERR_PASSPHRASE_NOT_PROVIDED = 30, ///< Error: Passphrase not provided NVM_ERR_NEW_PASSPHRASE_NOT_PROVIDED = 31, ///< Error: New passphrase not provided NVM_ERR_PASSPHRASES_DO_NOT_MATCH = 32, ///< Error: Passphrases do not match NVM_ERR_PASSPHRASE_TOO_LONG = 34, ///< Error: Passphrase too long NVM_ERR_ENABLE_SECURITY_NOT_ALLOWED = 35, ///< Error: Enable security not allowed NVM_ERR_CREATE_GOAL_NOT_ALLOWED = 36, ///< Error: Create goal not allowed NVM_ERR_INVALID_SECURITY_STATE = 37, ///< Error: Invalid security state NVM_ERR_INVALID_SECURITY_OPERATION = 38, ///< Error: Invalid security operation NVM_ERR_UNABLE_TO_GET_SECURITY_STATE = 39, ///< Error: Unable to get security state NVM_ERR_INCONSISTENT_SECURITY_STATE = 40, ///< Error: Inconsistent security state NVM_ERR_INVALID_PASSPHRASE = 41, ///< Error: Invalid passphrase NVM_ERR_SECURITY_USER_PP_COUNT_EXPIRED = 42, ///< Error: Security count for user passphrase expired NVM_ERR_SPI_ACCESS_NOT_ENABLED = 43, ///< Error: PMem module SPI access not enabled NVM_ERR_SECURE_ERASE_NAMESPACE_EXISTS = 44, ///< Error: Namespace exists - cannot execute request NVM_ERR_SECURITY_MASTER_PP_COUNT_EXPIRED = 45, ///< Error: Security count for master passphrase expired NVM_ERR_FLASH_SPI_NO_LONGER_SUPPORTED = 46, ///< Error: PMem module FlashSPI recovery is no longer supported NVM_ERR_IMAGE_FILE_NOT_COMPATIBLE_TO_CTLR_STEPPING = 59, ///< Error: Image not compatible with this PMem module NVM_ERR_FILENAME_NOT_PROVIDED = 60, ///< Error: Filename not provided NVM_SUCCESS_IMAGE_EXAMINE_OK = 61, ///< Success: Image OK NVM_ERR_IMAGE_FILE_NOT_VALID = 62, ///< Error: Image file not valid NVM_ERR_IMAGE_EXAMINE_LOWER_VERSION = 63, ///< Error: Image examine lower version invalid NVM_ERR_IMAGE_EXAMINE_INVALID = 64, ///< Error: Image invalid NVM_ERR_FIRMWARE_API_NOT_VALID = 65, ///< Error: Firmware API version not valid NVM_ERR_FIRMWARE_VERSION_NOT_VALID = 66, ///< Error: Firmware version not valid NVM_ERR_FIRMWARE_TOO_LOW_FORCE_REQUIRED = 67, ///< Error: Firmware version to low. Force option required NVM_ERR_FIRMWARE_ALREADY_LOADED = 68, ///< Error: Firmware already loaded NVM_ERR_FIRMWARE_FAILED_TO_STAGE = 69, ///< Error: Firmware failed to stage NVM_ERR_SENSOR_NOT_VALID = 70, ///< Error: Sensor not valid NVM_ERR_SENSOR_MEDIA_TEMP_OUT_OF_RANGE = 71, ///< Error: Sensor media temperature out of range NVM_ERR_SENSOR_CONTROLLER_TEMP_OUT_OF_RANGE = 72, ///< Error: Sensor controller temperature out of range NVM_ERR_SENSOR_CAPACITY_OUT_OF_RANGE = 73, ///< Error: Capacity out of range NVM_ERR_SENSOR_ENABLED_STATE_INVALID_VALUE = 74, ///< Error: Sensor invalid value NVM_ERR_ERROR_INJECTION_BIOS_KNOB_NOT_ENABLED = 75, ///< Error: BIOS error injection knob is not enabled NVM_SUCCESS_REQUIRES_POWER_CYCLE = 76, ///< Success. A power cycle is required for all changes to take effect. NVM_ERR_MEDIA_NOT_ACCESSIBLE = 87, ///< Error: Media not accessible NVM_ERR_MEDIA_DISABLED = 90, ///< Error: Media disabled NVM_ERR_MEDIA_NOT_ACCESSIBLE_CANNOT_CONTINUE = 91, ///< Error: Media not accessible. Replace PMem module to continue NVM_ERR_PCD_CURR_CONF_MISSING = 92, ///< Error: One or more PMem modules have invalid PCD data NVM_ERR_NMFM_RATIO_GREATER_THAN_ONE = 93, ///< Error: The requested memory mode size is below the NM:FM limit of 1:1 NVM_WARN_PMEM_MODULE_NOT_PAIRED_FOR_2LM = 94, ///< Warning: PMem module unusable for 2LM since it is not paired with a DDR on the iMc NVM_WARN_NMFM_RATIO_LOWER_VIOLATION_1to3_6 = 95, ///< Warning: The requested memory mode size is below the recommended NM:FM limit of 1:3.6 NVM_WARN_NMFM_RATIO_UPPER_VIOLATION_1to16 = 96, ///< Warning: The requested memory mode size is above the recommended NM:FM limit of 1:16 NVM_WARN_GOAL_CREATION_SECURITY_UNLOCKED = 97, ///< Warning: Goal will not be applied unless security is disabled prior to platform firmware (BIOS) provisioning! NVM_WARN_REGION_MAX_PM_INTERLEAVE_SETS_EXCEEDED = 98, ///< Warning: Interleave Sets cannot exceed MaxPMInterleaveSetsPerDie per Socket due to platform limitation NVM_WARN_REGION_MAX_AD_PM_INTERLEAVE_SETS_EXCEEDED = 99, ///< Warning: Interleave Sets cannot exceed MaxPMInterleaveSetsPerDie per Socket due to platform limitation for AD Interleaved mode NVM_WARN_REGION_MAX_AD_NI_PM_INTERLEAVE_SETS_EXCEEDED = 100, ///< Warning: Interleave Sets cannot exceed MaxPMInterleaveSetsPerDie per Socket due to platform limitation for AD Non-Interleaved mode NVM_WARN_REGION_AD_NI_PM_INTERLEAVE_SETS_REDUCED = 101, ///< Warning: Reducing the number of AppDirect2 (AD non-interleaved) regions created in AD interleaved mode request when MaxPMInterleaveSetsPerDie limit exceeded NVM_ERR_REGION_MAX_PM_INTERLEAVE_SETS_EXCEEDED = 102, ///< Error: Interleave Sets cannot exceed MaxPMInterleaveSetsPerDie per Socket due to platform limitation (error if existing regions + new region goals for specific PMem modules greater then MaxPMInterleaveSetsPerDie limit) NVM_WARN_IMC_DDR_PMM_NOT_PAIRED = 104, ///< Error: PMM and DDR4 missing on iMC NVM_ERR_PCD_BAD_DEVICE_CONFIG = 105, ///< Error: Bad PCD config NVM_ERR_REGION_GOAL_CONF_AFFECTS_UNSPEC_DIMM = 106, ///< Error: Goal config affects unspecified PMem module NVM_ERR_REGION_CURR_CONF_AFFECTS_UNSPEC_DIMM = 107, ///< Error: Current config affects unspecified PMem module NVM_ERR_REGION_GOAL_CURR_CONF_AFFECTS_UNSPEC_DIMM = 108, ///< Error: Current and goal config affects unspecified PMem module NVM_ERR_REGION_CONF_APPLYING_FAILED = 109, ///< Error: Failed to apply goal NVM_ERR_REGION_CONF_UNSUPPORTED_CONFIG = 110, ///< Error: Unsupported config NVM_ERR_REGION_NOT_FOUND = 111, ///< Error: Region not found NVM_ERR_PLATFORM_NOT_SUPPORT_MANAGEMENT_SOFT = 112, ///< Error: Platform does not support PMM software NVM_ERR_PLATFORM_NOT_SUPPORT_2LM_MODE = 113, ///< Error: Platform does not support MemoryMode NVM_ERR_PLATFORM_NOT_SUPPORT_PM_MODE = 114, ///< Error: Platform does not support persistent memory mode NVM_ERR_REGION_CURR_CONF_EXISTS = 115, ///< Error: Current config exists NVM_ERR_REGION_SIZE_TOO_SMALL_FOR_INT_SET_ALIGNMENT = 116, ///< Error: Region size too small for interleave set alignment NVM_ERR_PLATFORM_NOT_SUPPORT_SPECIFIED_INT_SIZES = 117, ///< Error: Platform does not support specified interleave sizes NVM_ERR_PLATFORM_NOT_SUPPORT_DEFAULT_INT_SIZES = 118, ///< Error: Platform does not support default interleave sizes NVM_ERR_REGION_NOT_HEALTHY = 119, ///< Error: Region not healthy NVM_ERR_REGION_NOT_ENOUGH_SPACE_FOR_PM_NAMESPACE = 121, ///< Error: Not enough space for persistent namespace NVM_ERR_REGION_NO_GOAL_EXISTS_ON_DIMM = 122, ///< Error: Goal does not exist on PMem module NVM_ERR_RESERVE_DIMM_REQUIRES_AT_LEAST_TWO_DIMMS = 123, ///< Error: Reserve PMem module requires at least 2 PMem modules NVM_ERR_REGION_GOAL_NAMESPACE_EXISTS = 124, ///< Error: Namespace exists NVM_ERR_REGION_REMAINING_SIZE_NOT_IN_LAST_PROPERTY = 125, ///< Error: Remaining size not in last property NVM_ERR_PERS_MEM_MUST_BE_APPLIED_TO_ALL_DIMMS = 126, ///< Error: Persistent memory must be applied to all PMem modules NVM_WARN_MAPPED_MEM_REDUCED_DUE_TO_CPU_SKU = 127, ///< Warning: Mapped memory reduced due to CPU SKU limit NVM_ERR_REGION_GOAL_AUTO_PROV_ENABLED = 128, ///< Error: Automatic provision enabled NVM_ERR_CREATE_NAMESPACE_NOT_ALLOWED = 129, ///< Error: Create namespace not allowed NVM_ERR_OPEN_FILE_WITH_WRITE_MODE_FAILED = 130, ///< Error: Failed to open file with write mode NVM_ERR_DUMP_NO_CONFIGURED_DIMMS = 131, ///< Error: No configured PMem modules NVM_ERR_DUMP_FILE_OPERATION_FAILED = 132, ///< Error: File IO failed NVM_ERR_LOAD_VERSION = 140, ///< Error: Invalid version NVM_ERR_LOAD_INVALID_DATA_IN_FILE = 141, ///< Error: Invalid data in file NVM_ERR_LOAD_IMPROPER_CONFIG_IN_FILE = 142, ///< Error: Improper config in file NVM_ERR_LOAD_DIMM_COUNT_MISMATCH = 148, ///< Error: Mismatch in PMem modules NVM_ERR_DIMM_SKU_MODE_MISMATCH = 151, ///< Error: SKU mode mismatch NVM_ERR_DIMM_SKU_SECURITY_MISMATCH = 152, ///< Error: SKU security mismatch NVM_ERR_NONE_DIMM_FULFILLS_CRITERIA = 168, ///< Error: No PMem module matches request NVM_ERR_UNSUPPORTED_BLOCK_SIZE = 171, ///< Error: Unsupported block size NVM_ERR_INVALID_NAMESPACE_CAPACITY = 174, ///< Error: Invalid namespace capacity NVM_ERR_NOT_ENOUGH_FREE_SPACE = 175, ///< Error: Not enough free space NVM_ERR_NAMESPACE_CONFIGURATION_BROKEN = 176, ///< Error: Namespace config broken NVM_ERR_NAMESPACE_DOES_NOT_EXIST = 177, ///< Error: Namespace does not exist NVM_ERR_NAMESPACE_COULD_NOT_UNINSTALL = 178, ///< Error: Namespace could not uninstall NVM_ERR_NAMESPACE_COULD_NOT_INSTALL = 179, ///< Error: Namespace could not install NVM_ERR_NAMESPACE_READ_ONLY = 180, ///< Error: Namespace read only NVM_ERR_PLATFORM_NOT_SUPPORT_BLOCK_MODE = 181, ///< Error: Platform does not support block mode NVM_WARN_BLOCK_MODE_DISABLED = 182, ///< Warning: Block mode disabled NVM_ERR_NAMESPACE_TOO_SMALL_FOR_BTT = 183, ///< Error: Namespace too small for BTT NVM_ERR_NOT_ENOUGH_FREE_SPACE_BTT = 184, ///< Error: Not enough free space for BTT NVM_ERR_FAILED_TO_UPDATE_BTT = 185, ///< Error: Failed to update BTT NVM_ERR_BADALIGNMENT = 186, ///< Error: Bad alignment NVM_ERR_RENAME_NAMESPACE_NOT_SUPPORTED = 187, ///< Error: Rename namespace not supported NVM_ERR_FAILED_TO_INIT_NS_LABELS = 188, ///< Error: Failed to initialize namespace labels NVM_ERR_FW_DBG_LOG_FAILED_TO_GET_SIZE = 195, ///< Error: Failed to get log size NVM_ERR_FW_DBG_SET_LOG_LEVEL_FAILED = 196, ///< Error: Failed to set log level NVM_INFO_FW_DBG_LOG_NO_LOGS_TO_FETCH = 197, ///< Error: No debug logs NVM_ERR_FAILED_TO_FETCH_ERROR_LOG = 200, ///< Error: Failed to fetch error log NVM_SUCCESS_NO_ERROR_LOG_ENTRY = 201, ///< Info: Request to retrieve entry was successful, however log was empty NVM_SUCCESS_NO_COMMAND_ACCESS_POLICY_ENTRY = 202, ///< Info: Request to retrieve entry was successful, however no entries found NVM_SUCCESS_NO_COMMAND_EFFECT_LOG_ENTRY = 203, ///< Info: Request to retrieve entry was successful, however log was empty NVM_ERR_SMART_FAILED_TO_GET_SMART_INFO = 220, ///< Error: Failed to get smart info NVM_WARN_SMART_NONCRITICAL_HEALTH_ISSUE = 221, ///< Warning: Non-critical health issue NVM_ERR_SMART_CRITICAL_HEALTH_ISSUE = 222, ///< Error: Critical health issue NVM_ERR_SMART_FATAL_HEALTH_ISSUE = 223, ///< Error: Fatal health issue NVM_ERR_SMART_READ_ONLY_HEALTH_ISSUE = 224, ///< Error: Read-only health issue NVM_ERR_SMART_UNKNOWN_HEALTH_ISSUE = 225, ///< Error: Unknown health issue NVM_ERR_FW_SET_OPTIONAL_DATA_POLICY_FAILED = 230, ///< Error: Set data policy failed NVM_ERR_INVALID_OPTIONAL_DATA_POLICY_STATE = 231, ///< Error: Invalid data policy state NVM_ERR_FAILED_TO_GET_DIMM_INFO = 235, ///< Error: Failed to get PMem module info NVM_ERR_FAILED_TO_GET_DIMM_REGISTERS = 240, ///< Error: Failed to get PMem module registers NVM_ERR_SMBIOS_DIMM_ENTRY_NOT_FOUND_IN_NFIT = 241, ///< Error: SMBIOS entry not found in NFIT NVM_OPERATION_IN_PROGRESS = 250, ///< Error: Operation in progress NVM_ERR_INCOMPATIBLE_SKU_ON_MODULE = 251, ///< Error: This module's SKU is incompatible with the SKU(s) of other installed module(s). NVM_ERR_GET_PCD_FAILED = 260, ///< Error: Get PCD failed NVM_ERR_ARS_IN_PROGRESS = 261, ///< Error: ARS in progress NVM_ERR_APPDIRECT_IN_SYSTEM = 262, ///< Error: AppDirect in system NVM_ERR_OPERATION_NOT_SUPPORTED_BY_MIXED_SKU = 263, ///< Error: Operation not supported by mixed SKUs NVM_ERR_FW_GET_FA_UNSUPPORTED = 264, ///< Error: NVM_ERR_FW_GET_FA_DATA_FAILED = 265, ///< Error: NVM_ERR_API_NOT_SUPPORTED = 266, ///< Error: API not supported NVM_ERR_UNKNOWN = 267, ///< Error: Unknown NVM_ERR_INVALID_PERMISSIONS = 268, ///< Error: Invalid permissions NVM_ERR_BAD_DEVICE = 269, ///< Error: Bad device NVM_ERR_BUSY_DEVICE = 270, ///< Error: Busy device NVM_ERR_GENERAL_OS_DRIVER_FAILURE = 271, ///< Error: General OS driver failure NVM_ERR_NO_MEM = 272, ///< Error: No memory NVM_ERR_BAD_SIZE = 273, ///< Error: Bad size NVM_ERR_TIMEOUT = 274, ///< Error: Timeout NVM_ERR_DATA_TRANSFER = 275, ///< Error: Data transfer failed NVM_ERR_GENERAL_DEV_FAILURE = 276, ///< Error: General device failure NVM_ERR_BAD_FW = 277, ///< Error: Bad FW NVM_ERR_DRIVER_FAILED = 288, ///< Error: Driver failed NVM_ERR_DRIVERFAILED = 288, ///< Error: Obsolete: Use NVM_ERR_DRIVER_FAILED NVM_ERR_INVALIDPARAMETER = 289, ///< Error: Obsolete: Use NVM_ERR_INVALID_PARAMETER NVM_ERR_OPERATION_NOT_SUPPORTED = 290, ///< Error: Operation not supported NVM_ERR_RETRY_SUGGESTED = 291, ///< Error: Retry suggested NVM_ERR_SPD_NOT_ACCESSIBLE = 300, ///< Error: SPD not accessible NVM_ERR_INCOMPATIBLE_HARDWARE_REVISION = 301, ///< Error: Incompatible hardware revision NVM_SUCCESS_NO_EVENT_FOUND = 302, ///< Error: No events found in the event log NVM_ERR_FILE_NOT_FOUND = 303, ///< Error: No events found in the event log NVM_ERR_OVERWRITE_DIMM_IN_PROGRESS = 304, ///< Error: No events found in the event log NVM_ERR_FWUPDATE_IN_PROGRESS = 305, ///< Error: No events found in the event log NVM_ERR_UNKNOWN_LONG_OP_IN_PROGRESS = 306, ///< Error: No events found in the event log NVM_ERR_LONG_OP_ABORTED_OR_REVISION_FAILURE = 307, ///< Error: long op was aborted NVM_ERR_FW_UPDATE_AUTH_FAILURE = 308, ///< Error: fw image authentication failed NVM_ERR_UNSUPPORTED_COMMAND = 309, ///< Error: unsupported command NVM_ERR_DEVICE_ERROR = 310, ///< Error: device error NVM_ERR_TRANSFER_ERROR = 311, ///< Error: transfer error NVM_ERR_UNABLE_TO_STAGE_NO_LONGOP = 312, ///< Error: FW image rejected during staging. Rerun FW update for rejection reason NVM_ERR_LONG_OP_UNKNOWN = 313, ///< Error: a long operation code is unknown NVM_ERR_PCD_DELETE_DENIED = 314, ///< Error: API not supported NVM_ERR_MIXED_GENERATIONS_NOT_SUPPORTED = 315, ///< Error: Operation does not work when PMem module that are different generations NVM_ERR_DIMM_HEALTHY_FW_NOT_RECOVERABLE = 316, ///< Error: An attempt to recover FW on a healthy PMem module NVM_ERR_PLATFORM_NOT_SUPPORT_MIXED_MODE = 317, ///< Error: Platform does not support mixed memory mode NVM_WARN_NMFM_RATIO_LOWER_VIOLATION_1to2 = 318, ///< Warning: The requested memory mode size is below the recommended NM:FM limit of 1:2 NVM_WARN_NMFM_RATIO_UPPER_VIOLATION_1to8 = 319, ///< Warning: The requested memory mode size is above the recommended NM:FM limit of 1:8 NVM_ERR_MASTER_PASSPHRASE_NOT_SET = 320, ///< Error: Master Passphrase is enabled but not set. NVM_ERR_INCOMPATIBLE_SOFTWARE_REVISION = 321, ///< Error: This version of ipmctl is incompatible with the UEFI platform firmware. Please upgrade to a newer version of ipmctl. NVM_ERR_INIT_FAILED_NO_MODULES_FOUND = 322, ///< Error: Initialization failed. No PMem modules in the system. NVM_WARN_PMTT_TABLE_NOT_FOUND = 323, ///< PMTT table is not found. BIOS might reject goal request upon reboot for SKU limit or NM:FM violation NVM_LAST_STATUS_VALUE } NvmStatusCode; typedef struct _PMON_REGISTERS { /** This will specify whether or not to return the extra smart data along with the PMON Counter data. - 0x0 - No Smart Data DDRT or Media. - 0x1 - DDRT Data only to be returned. - 0x2 - Media Data only to be returned. - 0x3 - DDRT and Media Data to be returned. - All other values reserved. **/ unsigned char SmartDataMask; unsigned char Reserved1[3]; /** This will specify which group that is currently enabled. If no groups are enabled Group F will be returned. **/ unsigned char GroupEnabled; unsigned char Reserved2[19]; unsigned int PMON4Counter; unsigned int PMON5Counter; unsigned char Reserved3[4]; unsigned int PMON7Counter; unsigned int PMON8Counter; unsigned int PMON9Counter; unsigned char Reserved4[16]; unsigned int PMON14Counter; unsigned char Reserved5[4]; /** DDR-T Reads for current power cycle **/ unsigned long long DDRTRD; /** DDR-T Writes for current power cycle **/ unsigned long long DDRTWR; /** Media Reads for current power cycle **/ unsigned long long MERD; /** Media Writes for current power cycle **/ unsigned long long MEWR; /** Current Media temperature **/ unsigned short MTP; /** Current Controller temperature **/ unsigned short CTP; unsigned char Reserved[20]; }PMON_REGISTERS; #endif /** _NVM_SHARED_DEFS_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/NvmStatus.c000066400000000000000000001337351440615110200205540ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include "NvmTypes.h" #include #include "NvmStatus.h" #include "Convert.h" /** Translate ObjectType from CLI command into Unicode string representing its brief description. @param[in] HiiHandle handle to the HII database that contains NvmStatusStrings @param[in] ObjectType the status code returned from a NVM command. @retval Pointer to a decoded string. Memory is dynamically allocated. It should be freed by caller. **/ STATIC CHAR16 * GetObjectTypeString( IN EFI_HANDLE HiiHandle, IN UINT8 ObjectType ) { CHAR16 *pObjectTypeString = NULL; switch (ObjectType) { case ObjectTypeSocket: pObjectTypeString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_SOCKET), NULL); break; case ObjectTypeDimm: pObjectTypeString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_PMM), NULL); break; case ObjectTypeNamespace: pObjectTypeString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_NAMESPACE), NULL); break; case ObjectTypeRegion: pObjectTypeString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_REGION), NULL); break; default: pObjectTypeString = CatSPrint(NULL, L""); break; } return pObjectTypeString; } /** Create command status as with specified command message. Function displays per PMem module status if such exists and summarizes status for the whole command. Memory allocated for status message and command status is freed after status is displayed. @param[in] HiiHandle handle to the HII database that contains NvmStatusStrings @param[in] pStatusMessage String with command information @param[in] pStatusPreposition String with preposition @param[in] pCommandStatus Command status data @param[in] ObjectIdNumberPreferred Use Object ID number if true, use Object ID string otherwise @param[in] DoNotPrintGeneralStatusSuccessCode If true, pCommandStatus->GeneralStatus is success, and there are no other nvm statuses set, then don't print out a success message. @param[out] ppOutputMessage buffer where output will be saved Warning: ppOutputMessage - should be freed in caller. @retval EFI_INVALID_PARAMETER pCommandStatus is NULL @retval EFI_SUCCESS All Ok **/ EFI_STATUS CreateCommandStatusString( IN EFI_HANDLE HiiHandle, IN CONST CHAR16 *pStatusMessage, IN CONST CHAR16 *pStatusPreposition, IN COMMAND_STATUS *pCommandStatus, IN BOOLEAN ObjectIdNumberPreferred, IN BOOLEAN DoNotPrintGeneralStatusSuccessCode, OUT CHAR16 **ppOutputMessage ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LIST_ENTRY *pObjectStatusNode = NULL; OBJECT_STATUS *pObjectStatus = NULL; CHAR16 *pObjectTypeString = NULL; CHAR16 *pSingleStatusCodeMessage = NULL; CHAR16 *pAllStatusCodeMessages = NULL; CHAR16 *pPrefixString = NULL; CHAR16 *pCurrentString = NULL; CHAR16 ObjectStr[MAX_OBJECT_ID_STR_LEN]; CHAR16 *pFailedString = NULL; CHAR16 *pErrorString = NULL; CHAR16 *pExecuteSuccessString = NULL; ZeroMem(ObjectStr, sizeof(ObjectStr)); NVDIMM_ENTRY(); if (pCommandStatus == NULL || ppOutputMessage == NULL) { goto Finish; } if (pCommandStatus->ObjectStatusCount == 0) { if (!NVM_ERROR(pCommandStatus->GeneralStatus)) { if (DoNotPrintGeneralStatusSuccessCode) { pCurrentString = CatSPrint(pCurrentString, L""); } else { pExecuteSuccessString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_EXECUTE_SUCCESS), NULL); pCurrentString = CatSPrint(pCurrentString, FORMAT_STR_SPACE FORMAT_STR_NL, pStatusMessage, pExecuteSuccessString); FREE_POOL_SAFE(pExecuteSuccessString); } } else { pSingleStatusCodeMessage = GetSingleNvmStatusCodeMessage(HiiHandle, pCommandStatus->GeneralStatus); if ((pCommandStatus->GeneralStatus == NVM_ERR_MANAGEABLE_DIMM_NOT_FOUND) || (pCommandStatus->GeneralStatus == NVM_ERR_DIMM_NOT_FOUND)) { pCurrentString = CatSPrint(pCurrentString, FORMAT_STR_NL, pSingleStatusCodeMessage); } else { pFailedString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_FAILED), NULL); pErrorString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERROR), NULL); pCurrentString = CatSPrint(pCurrentString, FORMAT_STR_SPACE FORMAT_STR L": " FORMAT_STR L" %d - " FORMAT_STR, pStatusMessage, pFailedString, pErrorString, pCommandStatus->GeneralStatus, pSingleStatusCodeMessage); FREE_POOL_SAFE(pFailedString); FREE_POOL_SAFE(pErrorString); } FREE_POOL_SAFE(pSingleStatusCodeMessage); } } else { LIST_FOR_EACH(pObjectStatusNode, &pCommandStatus->ObjectStatusList) { pObjectStatus = OBJECT_STATUS_FROM_NODE(pObjectStatusNode); pObjectTypeString = GetObjectTypeString(HiiHandle, pObjectStatus->ObjectType); ReturnCode = GetPreferredValueAsString( pObjectStatus->ObjectId, (pObjectStatus->IsObjectIdStr) ? pObjectStatus->ObjectIdStr : NULL, ObjectIdNumberPreferred, ObjectStr, MAX_OBJECT_ID_STR_LEN ); if (EFI_ERROR(ReturnCode)) { goto Finish; } if ((pObjectTypeString == NULL) || (pObjectTypeString[0] == L'\0')) { pPrefixString = CatSPrint(NULL, FORMAT_STR FORMAT_STR FORMAT_STR FORMAT_STR L": ", pStatusMessage, pStatusPreposition, pObjectTypeString, ObjectStr); } else { pPrefixString = CatSPrint(NULL, FORMAT_STR FORMAT_STR L" " FORMAT_STR L" " FORMAT_STR L": ", pStatusMessage, pStatusPreposition, pObjectTypeString, ObjectStr); } pAllStatusCodeMessages = GetAllNvmStatusCodeMessages(HiiHandle, pObjectStatus, pPrefixString); pCurrentString = CatSPrintClean(pCurrentString, FORMAT_STR, pAllStatusCodeMessages); FREE_POOL_SAFE(pObjectTypeString); FREE_POOL_SAFE(pPrefixString); FREE_POOL_SAFE(pAllStatusCodeMessages); } } pCurrentString = CatSPrintClean(pCurrentString, FORMAT_STR_NL, pCommandStatus->StatusDetails); *ppOutputMessage = pCurrentString; ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pObjectTypeString); FREE_POOL_SAFE(pAllStatusCodeMessages); FREE_POOL_SAFE(pPrefixString); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Translate NVM operation return code into Unicode string representing its brief description. @param[in] EFI HANDLE the HiiHandle to the HII database that contains NvmStatus strings @param[in] Code the status code returned from a NVM command. @retval Pointer to a decoded string. Memory is dynamically allocated. It should be freed by caller. **/ CHAR16 * GetSingleNvmStatusCodeMessage( IN EFI_HANDLE HiiHandle, IN NvmStatusCode Code ) { CHAR16 *pTempString = NULL; CHAR16 *pTempString1 = NULL; switch (Code) { case NVM_SUCCESS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_SUCCESS), NULL); case NVM_SUCCESS_FW_RESET_REQUIRED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_SUCCESS_FW_RESET_REQUIRED), NULL); case NVM_ERR_OPERATION_NOT_STARTED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_OPERATION_NOT_STARTED), NULL); case NVM_ERR_OPERATION_FAILED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_OPERATION_FAILED), NULL); case NVM_ERR_INVALIDPARAMETER: case NVM_ERR_INVALID_PARAMETER: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_INVALID_PARAMETER), NULL); case NVM_ERR_FORCE_REQUIRED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FORCE_REQUIRED), NULL); case NVM_ERR_DIMM_NOT_FOUND: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_DIMM_NOT_FOUND), NULL); case NVM_ERR_MANAGEABLE_DIMM_NOT_FOUND: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_MANAGEABLE_DIMM_NOT_FOUND), NULL); case NVM_ERR_DIMM_EXCLUDED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_DIMM_EXCLUDED), NULL); case NVM_ERR_NO_USABLE_DIMMS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_NO_USABLE_DIMMS), NULL); case NVM_ERR_FIRMWARE_API_NOT_VALID: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FIRMWARE_API_NOT_VALID), NULL); case NVM_ERR_DIMM_ID_DUPLICATED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_DIMM_ID_DUPLICATED), NULL); case NVM_ERR_SOCKET_ID_NOT_VALID: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SOCKET_ID_NOT_VALID), NULL); case NVM_ERR_SOCKET_ID_INCOMPATIBLE_W_DIMM_ID: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SOCKET_ID_INCOMPATIBLE_W_DIMM_ID), NULL); case NVM_ERR_SOCKET_ID_DUPLICATED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SOCKET_ID_DUPLICATED), NULL); case NVM_ERR_INVALID_PASSPHRASE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_INVALID_PASSPHRASE), NULL); case NVM_ERR_COMMAND_NOT_SUPPORTED_BY_THIS_SKU: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_COMMAND_NOT_SUPPORTED_BY_THIS_SKU), NULL); case NVM_ERR_CONFIG_NOT_SUPPORTED_BY_CURRENT_SKU: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_CONFIG_NOT_SUPPORTED_BY_CURRENT_SKU), NULL); case NVM_ERR_SECURITY_USER_PP_COUNT_EXPIRED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SECURITY_USER_PP_COUNT_EXPIRED), NULL); case NVM_ERR_SECURITY_MASTER_PP_COUNT_EXPIRED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SECURITY_MASTER_PP_COUNT_EXPIRED), NULL); case NVM_ERR_SPI_ACCESS_NOT_ENABLED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SPI_ACCESS_NOT_ENABLED), NULL); case NVM_ERR_SECURE_ERASE_NAMESPACE_EXISTS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SECURE_ERASE_NAMESPACE_EXISTS), NULL); case NVM_ERR_FLASH_SPI_NO_LONGER_SUPPORTED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FLASH_SPI_NO_LONGER_SUPPORTED), NULL); case NVM_ERR_PASSPHRASE_TOO_LONG: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PASSPHRASE_TOO_LONG), NULL); case NVM_ERR_PASSPHRASE_NOT_PROVIDED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PASSPHRASE_NOT_PROVIDED), NULL); case NVM_ERR_PASSPHRASES_DO_NOT_MATCH: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PASSPHRASES_DO_NOT_MATCH), NULL); case NVM_ERR_SENSOR_NOT_VALID: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SENSOR_NOT_VALID), NULL); case NVM_ERR_SENSOR_CONTROLLER_TEMP_OUT_OF_RANGE: pTempString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_CONTROLLER_OUT_OF_RANGE), NULL); if (pTempString == NULL) { return pTempString; } pTempString1 = CatSPrintClean(NULL, pTempString, TEMPERATURE_THRESHOLD_MIN, TEMPERATURE_CONTROLLER_THRESHOLD_MAX); FREE_POOL_SAFE(pTempString); return pTempString1; case NVM_ERR_SENSOR_MEDIA_TEMP_OUT_OF_RANGE: pTempString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_MEDIA_OUT_OF_RANGE), NULL); if (pTempString == NULL) { return pTempString; } pTempString1 = CatSPrintClean(NULL, pTempString, TEMPERATURE_THRESHOLD_MIN, TEMPERATURE_MEDIA_THRESHOLD_MAX); FREE_POOL_SAFE(pTempString); return pTempString1; case NVM_ERR_SENSOR_CAPACITY_OUT_OF_RANGE: pTempString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_CAPACITY_OUT_OF_RANGE), NULL); if (pTempString == NULL) { return pTempString; } pTempString1 = CatSPrintClean(NULL, pTempString, CAPACITY_THRESHOLD_MIN, CAPACITY_THRESHOLD_MAX); FREE_POOL_SAFE(pTempString); return pTempString1; case NVM_ERR_SENSOR_ENABLED_STATE_INVALID_VALUE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SENSOR_ENABLED_STATE_INVALID_VALUE), NULL); case NVM_ERR_MEDIA_DISABLED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_MEDIA_DISABLED_VALUE), NULL); case NVM_ERR_MEDIA_NOT_ACCESSIBLE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_MEDIA_NOT_ACCESSIBLE), NULL); case NVM_ERR_MEDIA_NOT_ACCESSIBLE_CANNOT_CONTINUE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_MEDIA_NOT_ACCESSIBLE_CANNOT_CONTINUE), NULL); case NVM_ERR_PCD_CURR_CONF_MISSING: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PCD_CURR_CONF_MISSING), NULL); case NVM_ERR_ENABLE_SECURITY_NOT_ALLOWED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_ENABLE_SECURITY_NOT_ALLOWED), NULL); case NVM_ERR_CREATE_GOAL_NOT_ALLOWED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_CREATE_GOAL_NOT_ALLOWED), NULL); case NVM_ERR_INVALID_SECURITY_STATE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_INVALID_SECURITY_STATE), NULL); case NVM_ERR_INVALID_SECURITY_OPERATION: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_INVALID_SECURITY_OPERATION), NULL); case NVM_ERR_UNABLE_TO_GET_SECURITY_STATE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_UNABLE_TO_GET_SECURITY_STATE), NULL); case NVM_ERR_INCONSISTENT_SECURITY_STATE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_INCONSISTENT_SECURITY_STATE), NULL); case NVM_WARN_IMC_DDR_PMM_NOT_PAIRED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_WARN_MM_PMM_DDR_NOT_PAIRED), NULL); case NVM_WARN_PMEM_MODULE_NOT_PAIRED_FOR_2LM: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_WARN_PMEM_MODULE_NOT_PAIRED_FOR_2LM), NULL); case NVM_WARN_NMFM_RATIO_LOWER_VIOLATION_1to3_6: case NVM_WARN_NMFM_RATIO_LOWER_VIOLATION_1to2: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_WARN_NMFM_RATIO_LOWER_VIOLATION), NULL); case NVM_WARN_NMFM_RATIO_UPPER_VIOLATION_1to8: case NVM_WARN_NMFM_RATIO_UPPER_VIOLATION_1to16: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_WARN_NMFM_RATIO_UPPER_VIOLATION), NULL); case NVM_ERR_NMFM_RATIO_GREATER_THAN_ONE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_NMFM_RATIO_GREATER_THAN_ONE), NULL); case NVM_WARN_MAPPED_MEM_REDUCED_DUE_TO_CPU_SKU: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_WARN_REDUCED_CAPACITY_DUE_TO_SKU), NULL); case NVM_WARN_REGION_MAX_AD_PM_INTERLEAVE_SETS_EXCEEDED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_WARN_MAX_AD_PM_INTERLEAVE_SETS_EXCEEDED), NULL); case NVM_WARN_REGION_MAX_AD_NI_PM_INTERLEAVE_SETS_EXCEEDED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_WARN_MAX_AD_NI_PM_INTERLEAVE_SETS_EXCEEDED), NULL); case NVM_WARN_REGION_AD_NI_PM_INTERLEAVE_SETS_REDUCED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_WARN_AD_NI_PM_INTERLEAVE_SETS_REDUCED), NULL); case NVM_ERR_REGION_MAX_PM_INTERLEAVE_SETS_EXCEEDED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_MAX_PM_INTERLEAVE_SETS_EXCEEDED), NULL); case NVM_ERR_NAMESPACE_TOO_SMALL_FOR_BTT: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_NAMESPACE_TOO_SMALL_FOR_BTT), NULL); case NVM_ERR_PCD_BAD_DEVICE_CONFIG: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PCD_BAD_DEVICE_CONFIG), NULL); case NVM_ERR_REGION_GOAL_CONF_AFFECTS_UNSPEC_DIMM: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_REGION_GOAL_CONF_AFFECTS_UNSPEC_DIMM), NULL); case NVM_ERR_REGION_CURR_CONF_AFFECTS_UNSPEC_DIMM: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_REGION_CURR_CONF_AFFECTS_UNSPEC_DIMM), NULL); case NVM_ERR_REGION_GOAL_CURR_CONF_AFFECTS_UNSPEC_DIMM: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_REGION_GOAL_CURR_CONF_AFFECTS_UNSPEC_DIMM), NULL); case NVM_ERR_REGION_CONF_APPLYING_FAILED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_REGION_CONF_APPLYING_FAILED), NULL); case NVM_ERR_REGION_CONF_UNSUPPORTED_CONFIG: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_REGION_CONF_UNSUPPORTED_CONFIG), NULL); case NVM_ERR_REGION_NOT_FOUND: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_REGION_NOT_FOUND), NULL); case NVM_ERR_PLATFORM_NOT_SUPPORT_MANAGEMENT_SOFT: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_MANAGEMENT_SOFT), NULL); case NVM_ERR_PLATFORM_NOT_SUPPORT_2LM_MODE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_2LM_MODE), NULL); case NVM_ERR_PLATFORM_NOT_SUPPORT_MIXED_MODE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_MIXED_MODE), NULL); case NVM_ERR_PLATFORM_NOT_SUPPORT_PM_MODE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_PM_MODE), NULL); case NVM_ERR_REGION_CURR_CONF_EXISTS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_REGION_CURR_CONF_EXISTS), NULL); case NVM_ERR_REGION_SIZE_TOO_SMALL_FOR_INT_SET_ALIGNMENT: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_REGION_SIZE_TOO_SMALL_FOR_INT_SET_ALIGNMENT), NULL); case NVM_ERR_PLATFORM_NOT_SUPPORT_SPECIFIED_INT_SIZES: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_SPECIFIED_INT_SIZES), NULL); case NVM_ERR_PLATFORM_NOT_SUPPORT_DEFAULT_INT_SIZES: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_DEFAULT_INT_SIZES), NULL); case NVM_ERR_REGION_NOT_HEALTHY: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_REGION_NOT_HEALTHY), NULL); case NVM_ERR_REGION_NOT_ENOUGH_SPACE_FOR_PM_NAMESPACE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_REGION_NOT_ENOUGH_SPACE_FOR_PM_NAMESPACE), NULL); case NVM_ERR_REGION_NO_GOAL_EXISTS_ON_DIMM: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_REGION_NO_GOAL_EXISTS_ON_DIMM), NULL); case NVM_ERR_RESERVE_DIMM_REQUIRES_AT_LEAST_TWO_DIMMS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_RESERVE_DIMM_REQUIRES_AT_LEAST_TWO_DIMMS), NULL); case NVM_ERR_REGION_GOAL_NAMESPACE_EXISTS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_REGION_GOAL_NAMESPACE_EXISTS), NULL); case NVM_ERR_REGION_GOAL_AUTO_PROV_ENABLED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_CREATE_GOAL_AUTO_PROV_ENABLED), NULL); case NVM_ERR_CREATE_NAMESPACE_NOT_ALLOWED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_CREATE_NAMESPACE_NOT_ALLOWED), NULL); case NVM_ERR_REGION_REMAINING_SIZE_NOT_IN_LAST_PROPERTY: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_REGION_REMAINING_SIZE_NOT_IN_LAST_PROPERTY), NULL); case NVM_ERR_PERS_MEM_MUST_BE_APPLIED_TO_ALL_DIMMS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PERS_MEM_MUST_BE_APPLIED_TO_ALL_DIMMS), NULL); case NVM_ERR_OPEN_FILE_WITH_WRITE_MODE_FAILED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_OPEN_FILE_WITH_WRITE_MODE_FAILED), NULL); case NVM_ERR_DUMP_NO_CONFIGURED_DIMMS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_DUMP_NO_CONFIGURED_DIMMS), NULL); case NVM_ERR_DUMP_FILE_OPERATION_FAILED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_DUMP_FILE_OPERATION_FAILED), NULL); case NVM_ERR_LOAD_VERSION: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_LOAD_VERSION), NULL); case NVM_ERR_LOAD_INVALID_DATA_IN_FILE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_LOAD_INVALID_DATA_IN_FILE), NULL); case NVM_ERR_LOAD_IMPROPER_CONFIG_IN_FILE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_LOAD_IMPROPER_CONFIG_IN_FILE), NULL); case NVM_ERR_LOAD_DIMM_COUNT_MISMATCH: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_LOAD_DIMM_COUNT_MISMATCH), NULL); case NVM_ERR_IMAGE_EXAMINE_INVALID: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_IMAGE_EXAMINE_INVALID), NULL); case NVM_SUCCESS_IMAGE_EXAMINE_OK: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_SUCCESS_IMAGE_EXAMINE_OK), NULL); case NVM_ERR_IMAGE_EXAMINE_LOWER_VERSION: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_IMAGE_EXAMINE_LOWER_VERSION), NULL); case NVM_ERR_IMAGE_FILE_NOT_VALID: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_IMAGE_FILE_NOT_VALID), NULL); case NVM_ERR_DIMM_SKU_MODE_MISMATCH: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_DIMM_SKU_MODE_MISMATCH), NULL); case NVM_ERR_DIMM_SKU_SECURITY_MISMATCH: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_DIMM_SKU_SECURITY_MISMATCH), NULL); case NVM_ERR_OPERATION_NOT_SUPPORTED_BY_MIXED_SKU: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_OPERATION_NOT_SUPPORTED_BY_MIXED_SKU), NULL); case NVM_ERR_INCOMPATIBLE_SKU_ON_MODULE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_INCOMPATIBLE_SKU_ON_MODULE), NULL); case NVM_ERR_ERROR_INJECTION_BIOS_KNOB_NOT_ENABLED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_INJECTION_BIOS_KNOB_NOT_ENABLED), NULL); case NVM_ERR_NONE_DIMM_FULFILLS_CRITERIA: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_NONE_DIMM_FULFILLS_CRITERIA), NULL); case NVM_ERR_UNSUPPORTED_BLOCK_SIZE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_UNSUPPORTED_BLOCK_SIZE), NULL); case NVM_ERR_INVALID_NAMESPACE_CAPACITY: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_INVALID_NAMESPACE_CAPACITY), NULL); case NVM_ERR_NAMESPACE_DOES_NOT_EXIST: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_NAMESPACE_DOES_NOT_EXIST), NULL); case NVM_ERR_NAMESPACE_CONFIGURATION_BROKEN: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_NAMESPACE_CONFIGURATION_BROKEN), NULL); case NVM_ERR_NOT_ENOUGH_FREE_SPACE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_NOT_ENOUGH_FREE_SPACE), NULL); case NVM_ERR_NOT_ENOUGH_FREE_SPACE_BTT: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_NOT_ENOUGH_FREE_SPACE_BTT), NULL); case NVM_ERR_FAILED_TO_UPDATE_BTT: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FAILED_TO_UPDATE_BTT), NULL); case NVM_ERR_PLATFORM_NOT_SUPPORT_BLOCK_MODE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_BLOCK_MODE), NULL); case NVM_WARN_BLOCK_MODE_DISABLED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_WARN_BLOCK_MODE_DISABLED), NULL); case NVM_ERR_BADALIGNMENT: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_BADALIGNMENT), NULL); case NVM_ERR_RENAME_NAMESPACE_NOT_SUPPORTED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_RENAME_NAMESPACE_NOT_SUPPORTED), NULL); case NVM_ERR_FAILED_TO_INIT_NS_LABELS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FAILED_TO_INIT_NS_LABELS), NULL); case NVM_ERR_SMART_FAILED_TO_GET_SMART_INFO: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SMART_FAILED_TO_GET_SMART_INFO), NULL); case NVM_WARN_SMART_NONCRITICAL_HEALTH_ISSUE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_WARN_SMART_NONCRITICAL_HEALTH_ISSUE), NULL); case NVM_ERR_SMART_CRITICAL_HEALTH_ISSUE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SMART_CRITICAL_HEALTH_ISSUE), NULL); case NVM_ERR_SMART_FATAL_HEALTH_ISSUE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SMART_FATAL_HEALTH_ISSUE), NULL); case NVM_ERR_SMART_READ_ONLY_HEALTH_ISSUE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SMART_READ_ONLY_HEALTH_ISSUE), NULL); case NVM_ERR_SMART_UNKNOWN_HEALTH_ISSUE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SMART_UNKNOWN_HEALTH_ISSUE), NULL); case NVM_ERR_FAILED_TO_GET_DIMM_INFO: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FAILED_TO_GET_DIMM_INFO), NULL); case NVM_ERR_DIMM_HEALTHY_FW_NOT_RECOVERABLE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_DIMM_HEALTHY_FW_NOT_RECOVERABLE), NULL); case NVM_ERR_FW_SET_OPTIONAL_DATA_POLICY_FAILED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FW_SET_OPTIONAL_DATA_POLICY_FAILED), NULL); case NVM_ERR_INVALID_OPTIONAL_DATA_POLICY_STATE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_INVALID_OPTIONAL_DATA_POLICY_STATE), NULL); case NVM_ERR_FAILED_TO_GET_DIMM_REGISTERS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FAILED_TO_GET_DIMM_REGISTERS), NULL); case NVM_ERR_SMBIOS_DIMM_ENTRY_NOT_FOUND_IN_NFIT: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SMBIOS_DIMM_ENTRY_NOT_FOUND_IN_NFIT), NULL); case NVM_OPERATION_IN_PROGRESS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_OPERATION_IN_PROGRESS), NULL); case NVM_ERR_GET_PCD_FAILED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_GET_PCD_FAILED), NULL); case NVM_ERR_ARS_IN_PROGRESS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_ARS_IN_PROGRESS), NULL); case NVM_ERR_FWUPDATE_IN_PROGRESS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FWUPDATE_IN_PROGRESS), NULL); case NVM_ERR_OVERWRITE_DIMM_IN_PROGRESS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_OVERWRITE_DIMM_IN_PROGRESS), NULL); case NVM_ERR_UNKNOWN_LONG_OP_IN_PROGRESS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_UNKNOWN_LONG_OP_IN_PROGRESS), NULL); case NVM_ERR_LONG_OP_ABORTED_OR_REVISION_FAILURE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_LONG_OP_ABORTED_OR_REVISION_FAILURE), NULL); case NVM_ERR_FW_UPDATE_AUTH_FAILURE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FW_UPDATE_AUTH_FAILURE), NULL); case NVM_ERR_UNSUPPORTED_COMMAND: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_UNSUPPORTED_COMMAND), NULL); case NVM_ERR_DEVICE_ERROR: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_DEVICE_ERROR), NULL); case NVM_ERR_TRANSFER_ERROR: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_TRANSFER_ERROR), NULL); case NVM_ERR_UNABLE_TO_STAGE_NO_LONGOP: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_UNABLE_TO_STAGE_NO_LONGOP), NULL); case NVM_ERR_LONG_OP_UNKNOWN: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_LONG_OP_UNKNOWN), NULL); case NVM_ERR_APPDIRECT_IN_SYSTEM: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_APPDIRECT_IN_SYSTEM), NULL); case NVM_ERR_FIRMWARE_TOO_LOW_FORCE_REQUIRED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FIRMWARE_DOWNGRADE), NULL); case NVM_ERR_FIRMWARE_ALREADY_LOADED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FIRMWARE_ALREADY_LOADED), NULL); case NVM_ERR_FIRMWARE_FAILED_TO_STAGE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FIRMWARE_FAILED_TO_STAGE), NULL); case NVM_ERR_IMAGE_FILE_NOT_COMPATIBLE_TO_CTLR_STEPPING: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_VIEW_FIRMWARE_INCOMPATIBLE_TO_CTLR_STEPPING), NULL); case NVM_ERR_FIRMWARE_VERSION_NOT_VALID: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_VIEW_FIRMWARE_VERSION_NOT_VALID), NULL); case NVM_ERR_FW_DBG_LOG_FAILED_TO_GET_SIZE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FW_DBG_LOG_FAILED_TO_GET_SIZE), NULL); case NVM_ERR_FW_DBG_SET_LOG_LEVEL_FAILED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FW_SET_LOG_LEVEL_FAILED), NULL); case NVM_INFO_FW_DBG_LOG_NO_LOGS_TO_FETCH: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_INFO_FW_DBG_LOG_NO_LOGS_TO_FETCH), NULL); case NVM_ERR_API_NOT_SUPPORTED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_API_NOT_SUPPORTED), NULL); case NVM_ERR_PCD_DELETE_DENIED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_PCD_DELETE_DENIED), NULL); case NVM_ERR_UNKNOWN: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_UNKNOWN), NULL); case NVM_ERR_INVALID_PERMISSIONS: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_INVALID_PERMISSIONS), NULL); case NVM_ERR_BAD_DEVICE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_BAD_DEVICE), NULL); case NVM_ERR_BUSY_DEVICE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_BUSY_DEVICE), NULL); case NVM_ERR_GENERAL_OS_DRIVER_FAILURE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_GENERAL_OS_DRIVER_FAILURE), NULL); case NVM_ERR_NO_MEM: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_NO_MEM), NULL); case NVM_ERR_BAD_SIZE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_BAD_SIZE), NULL); case NVM_ERR_TIMEOUT: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_TIMEOUT), NULL); case NVM_ERR_DATA_TRANSFER: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_DATA_TRANSFER), NULL); case NVM_ERR_GENERAL_DEV_FAILURE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_GENERAL_DEV_FAILURE), NULL); case NVM_ERR_BAD_FW: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_BAD_FW), NULL); case NVM_ERR_DRIVER_FAILED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_DRIVERFAILED), NULL); case NVM_ERR_OPERATION_NOT_SUPPORTED: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_NOT_SUPPORTED), NULL); case NVM_ERR_SPD_NOT_ACCESSIBLE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_SPD_NOT_ACCESSIBLE), NULL); case NVM_ERR_INCOMPATIBLE_HARDWARE_REVISION: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_INCOMPATIBLE_HARDWARE_REVISION), NULL); case NVM_SUCCESS_NO_EVENT_FOUND: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_SUCCESS_NO_EVENT_FOUND), NULL); case NVM_ERR_FILE_NOT_FOUND: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_FILE_NOT_FOUND), NULL); case NVM_SUCCESS_REQUIRES_POWER_CYCLE: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_SUCCESS_REQUIRES_POWER_CYCLE), NULL); case NVM_ERR_MASTER_PASSPHRASE_NOT_SET: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_MASTER_PASSPHRASE_NOT_SET), NULL); case NVM_ERR_INCOMPATIBLE_SOFTWARE_REVISION: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_INCOMPATIBLE_SOFTWARE_REVISION), NULL); case NVM_ERR_INIT_FAILED_NO_MODULES_FOUND: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_INITIALIZATION_FAILED_NO_MODULES), NULL); case NVM_WARN_PMTT_TABLE_NOT_FOUND: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_WARN_PMTT_TABLE_NOT_FOUND), NULL); default: return HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_DEFAULT), NULL); } } /** Translate all NVM Statuses of operation into Unicode string representing its brief description. @param[in] HiiHandle - Handle to the Hii Database that contains the NvmStatus strings @param[in] pObjectStatus - Object status containing BitField with NVM Statuses @param[in] pPrefixString - prefix for all lines of statuses @retval Pointer to a decoded string. Memory is dynamically allocated. It should be freed by caller. **/ CHAR16 * GetAllNvmStatusCodeMessages( IN EFI_HANDLE HiiHandle, IN OBJECT_STATUS *pObjectStatus, IN CONST CHAR16 *pPrefixString ) { CHAR16 *pStatusStr = NULL; CHAR16 *pSingleStatusStr = NULL; CHAR16 *pErrorLevelStr = NULL; UINT64 Index = NVM_LAST_STATUS_VALUE; BOOLEAN CodeSet = FALSE; NVDIMM_ENTRY(); if (pObjectStatus == NULL || pPrefixString == NULL) { goto Finish; } for (Index = 0; Index < NVM_LAST_STATUS_VALUE; ++Index) { CodeSet = IsSetNvmStatus(pObjectStatus, Index); if (CodeSet) { pSingleStatusStr = GetSingleNvmStatusCodeMessage(HiiHandle, Index); if (Index == NVM_SUCCESS || Index == NVM_SUCCESS_FW_RESET_REQUIRED || Index == NVM_ERR_IMAGE_EXAMINE_INVALID || Index == NVM_SUCCESS_IMAGE_EXAMINE_OK || Index == NVM_ERR_IMAGE_EXAMINE_LOWER_VERSION || Index == NVM_ERR_FIRMWARE_TOO_LOW_FORCE_REQUIRED || Index == NVM_WARN_PMTT_TABLE_NOT_FOUND || Index == NVM_SUCCESS_REQUIRES_POWER_CYCLE) { // Don't print an "Error" string prefix pStatusStr = CatSPrintClean(pStatusStr, L"" FORMAT_STR L"" FORMAT_STR_NL, pPrefixString, pSingleStatusStr); } else { // Do print an "Error" string prefix pErrorLevelStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERROR), NULL); pStatusStr = CatSPrintClean(pStatusStr, L"" FORMAT_STR L"" FORMAT_STR L" %d - " FORMAT_STR_NL, pPrefixString, pErrorLevelStr, Index, pSingleStatusStr); } FREE_POOL_SAFE(pSingleStatusStr); } } Finish: FREE_POOL_SAFE(pErrorLevelStr); NVDIMM_EXIT(); return pStatusStr; } /** Clear Nvm status code for given object ID @param[in/out] pObjectStatus pointer to object status (with Nvm Status bit field) @param[in] ObjectId - object for clearing status @param[in] NvmStatusCode code to clear @retval TRUE - if Object Status has got code set @retval FALSE - else **/ VOID ClearNvmStatusForObject( IN OUT COMMAND_STATUS *pCommandStatus, IN UINT32 ObjectId, IN NvmStatusCode Code ) { OBJECT_STATUS *pObjectStatus = NULL; if (pCommandStatus == NULL) { NVDIMM_DBG("pCommandStatus = NULL, Invalid parameter"); } else { pObjectStatus = GetObjectStatus(pCommandStatus, ObjectId); if (pObjectStatus != NULL) { ClearNvmStatus(pObjectStatus, Code); } } } /** Clear Nvm status code @param[in/out] pObjectStatus pointer to object status (with Nvm Status bit field) @param[in] NvmStatusCode code to clear @retval TRUE - if Object Status has got code set @retval FALSE - else **/ VOID ClearNvmStatus( IN OUT OBJECT_STATUS *pObjectStatus, IN NvmStatusCode Code ) { CONST UINT32 Index = Code / 64; CONST UINT64 Mod = Code % 64; CONST UINT64 Bit = (UINT64)1 << Mod; if (pObjectStatus != NULL) { pObjectStatus->StatusBitField.BitField[Index] &= ~Bit; } } /** Set proper code in Object status @param[in/out] pObjectStatus pointer to object status (with Nvm Status bit field) @param[in] NvmStatusCode code to set **/ VOID SetNvmStatus( IN OUT OBJECT_STATUS *pObjectStatus, IN NvmStatusCode Code ) { CONST UINT32 Index = Code / 64; CONST UINT64 Mod = Code % 64; CONST UINT64 Bit = (UINT64)1 << Mod; if (pObjectStatus != NULL) { pObjectStatus->StatusBitField.BitField[Index] |= Bit; } } /** Check if Object status got proper NVM status code set. @param[in, out] pObjectStatus pointer to object status (with Nvm Status bit field) @param[in] ObjectId - object for checking status @param[in] NvmStatusCode code to check if is set @retval TRUE - if Object Status has got code set @retval FALSE - else **/ BOOLEAN IsSetNvmStatusForObject( IN OUT COMMAND_STATUS *pCommandStatus, IN UINT32 ObjectId, IN NVM_STATUS Status ) { OBJECT_STATUS *pObjectStatus = NULL; BOOLEAN IsSetObjectStatus = FALSE; NVDIMM_ENTRY(); if (pCommandStatus == NULL) { NVDIMM_DBG("pCommandStatus = NULL, Invalid parameter"); goto Finish; } pObjectStatus = GetObjectStatus(pCommandStatus, ObjectId); if (pObjectStatus == NULL) { goto Finish; } IsSetObjectStatus = IsSetNvmStatus(pObjectStatus, Status); Finish: NVDIMM_EXIT(); return IsSetObjectStatus; } /** Check if Object status got proper NVM status code set. @param[in] pObjectStatus pointer to object status (with Nvm Status bit field) @param[in] NvmStatusCode code to check if is set @retval TRUE - if Object Status has got code set @retval FALSE - else **/ BOOLEAN IsSetNvmStatus( IN OBJECT_STATUS *pObjectStatus, IN NvmStatusCode Code ) { CONST UINT32 Index = Code / 64; CONST UINT64 Mod = Code % 64; CONST UINT64 Bit = (UINT64)1 << Mod; BOOLEAN IsSet = FALSE; NVDIMM_ENTRY(); if (pObjectStatus == NULL) { goto Finish; } IsSet = (pObjectStatus->StatusBitField.BitField[Index] & Bit) != 0; Finish: NVDIMM_EXIT(); return IsSet; } /** Initialize command status structure. Allocate memory and assign default values. @param[in, out] ppCommandStatus pointer to address of the structure @retval EFI_OUT_OF_RESOURCES Unable to allocate memory @retval EFI_SUCCESS All Ok **/ EFI_STATUS InitializeCommandStatus( IN OUT COMMAND_STATUS **ppCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; COMMAND_STATUS *pCommandStatus = NULL; NVDIMM_ENTRY(); if (ppCommandStatus == NULL) { goto Finish; } CHECK_RESULT_MALLOC(pCommandStatus,(COMMAND_STATUS *)AllocateZeroPool(sizeof(*pCommandStatus)), Finish); pCommandStatus->GeneralStatus = NVM_ERR_OPERATION_NOT_STARTED; pCommandStatus->ObjectStatusCount = 0; InitializeListHead(&pCommandStatus->ObjectStatusList); *ppCommandStatus = pCommandStatus; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Free previously allocated and initialized command status structure @param[in] ppCommandStatus pointer to address of the structure **/ VOID FreeCommandStatus( IN COMMAND_STATUS **ppCommandStatus ) { VOID *pTemporaryNode = NULL; LIST_ENTRY *pObjectStatusNode = NULL; LIST_ENTRY *pObjectStatusNextNode = NULL; NVDIMM_ENTRY(); if (ppCommandStatus != NULL) { if (*ppCommandStatus != NULL) { LIST_FOR_EACH_SAFE(pObjectStatusNode, pObjectStatusNextNode, &((*ppCommandStatus)->ObjectStatusList)) { pTemporaryNode = OBJECT_STATUS_FROM_NODE(pObjectStatusNode); FREE_POOL_SAFE(pTemporaryNode); } } FREE_POOL_SAFE(*ppCommandStatus); } NVDIMM_EXIT(); } /** Add (or update) status for specified ID in command status list @param[in, out] pCommandStatus - command status @param[in] ObjectId - Id for specified object @param[in] pObjectIdStr - Id for specified object as string representation, OPTIONAL @param[in] ObjectIdStrLength - Max length of pObjectIdStr, OPTIONAL @param[in] Status - status for update/set @param[in] ObjectType - type for specified object **/ VOID SetObjStatus( IN OUT COMMAND_STATUS *pCommandStatus, IN UINT32 ObjectId, IN CHAR16 *pObjectIdStr OPTIONAL, IN UINT32 ObjectIdStrLength OPTIONAL, IN NVM_STATUS Status, IN OBJECT_TYPE ObjectType ) { OBJECT_STATUS *pObjectStatus = NULL; NVDIMM_ENTRY(); if (pCommandStatus == NULL) { NVDIMM_DBG("pCommandStatus = NULL, Invalid parameter"); goto Finish; } if (!IsListInitialized(pCommandStatus->ObjectStatusList)) { InitializeListHead(&pCommandStatus->ObjectStatusList); } pObjectStatus = GetObjectStatus(pCommandStatus, ObjectId); if (pObjectStatus != NULL) { SetNvmStatus(pObjectStatus, Status); ClearNvmStatus(pObjectStatus, NVM_ERR_OPERATION_NOT_STARTED); goto Finish; } pObjectStatus = AllocateZeroPool(sizeof(*pObjectStatus)); if (pObjectStatus == NULL) { NVDIMM_ERR("Out of memory"); goto Finish; } pObjectStatus->ObjectId = ObjectId; if (pObjectIdStr != NULL && StrLen(pObjectIdStr) > 0) { pObjectStatus->IsObjectIdStr = TRUE; StrnCpyS(pObjectStatus->ObjectIdStr, MAX_OBJECT_ID_STR_LEN, pObjectIdStr, MIN(ObjectIdStrLength, MAX_OBJECT_ID_STR_LEN) - 1); } else { pObjectStatus->IsObjectIdStr = FALSE; } pObjectStatus->ObjectType = ObjectType; SetNvmStatus(pObjectStatus, Status); pObjectStatus->Signature = OBJECT_STATUS_SIGNATURE; pObjectStatus->Progress = 0; InitializeListHead(&pObjectStatus->ObjectStatusNode); InsertTailList(&pCommandStatus->ObjectStatusList, &pObjectStatus->ObjectStatusNode); pCommandStatus->ObjectStatusCount++; pCommandStatus->GeneralStatus = Status; Finish: NVDIMM_EXIT(); } /** Set progress for specified ID in command status list @param[in, out] pCommandStatus - command status @param[in] ObjectId - Id for specified object @param[in] Progress - progress to set **/ VOID SetObjProgress( IN OUT COMMAND_STATUS *pCommandStatus, IN UINT32 ObjectId, IN UINT8 Progress ) { OBJECT_STATUS *pObjectStatus = NULL; NVDIMM_ENTRY(); if (pCommandStatus == NULL) { NVDIMM_DBG("pCommandStatus = NULL,Invalid parameter"); goto Finish; } if (!IsListInitialized(pCommandStatus->ObjectStatusList)) { InitializeListHead(&pCommandStatus->ObjectStatusList); } pObjectStatus = GetObjectStatus(pCommandStatus, ObjectId); if (pObjectStatus != NULL) { SetNvmStatus(pObjectStatus, NVM_OPERATION_IN_PROGRESS); pObjectStatus->Progress = Progress; goto Finish; } pObjectStatus = AllocateZeroPool(sizeof(*pObjectStatus)); if (pObjectStatus == NULL) { NVDIMM_ERR("Out of memory"); goto Finish; } pObjectStatus->ObjectId = ObjectId; SetNvmStatus(pObjectStatus, NVM_OPERATION_IN_PROGRESS); pObjectStatus->Signature = OBJECT_STATUS_SIGNATURE; pObjectStatus->Progress = Progress; InitializeListHead(&pObjectStatus->ObjectStatusNode); InsertTailList(&pCommandStatus->ObjectStatusList, &pObjectStatus->ObjectStatusNode); pCommandStatus->ObjectStatusCount++; pCommandStatus->GeneralStatus = NVM_OPERATION_IN_PROGRESS; Finish: NVDIMM_EXIT(); } /** Set general command status and zero status object counter @param[in, out] pCommandStatus - command status @param[in] Status - status for update/set **/ VOID ResetCmdStatus( IN OUT COMMAND_STATUS *pCommandStatus, IN NVM_STATUS Status ) { OBJECT_STATUS *pObjectStatus = NULL; LIST_ENTRY *pObjectStatusNode = NULL; LIST_ENTRY *pObjectStatusNextNode = NULL; NVDIMM_ENTRY(); if (pCommandStatus == NULL) { NVDIMM_DBG("pCommandStatus = NULL, Invalid parameter"); goto Finish; } if (IsListInitialized(pCommandStatus->ObjectStatusList) && !IsListEmpty(&pCommandStatus->ObjectStatusList)) { /** Free object status memory **/ LIST_FOR_EACH_SAFE(pObjectStatusNode, pObjectStatusNextNode, &pCommandStatus->ObjectStatusList) { pObjectStatus = OBJECT_STATUS_FROM_NODE(pObjectStatusNode); RemoveEntryList(pObjectStatusNode); FREE_POOL_SAFE(pObjectStatus); } } pCommandStatus->ObjectStatusCount = 0; pCommandStatus->GeneralStatus = Status; Finish: NVDIMM_EXIT(); } /** Set general command status @param[in, out] pCommandStatus - command status @param[in] Status - status for update/set **/ VOID SetCmdStatus( IN OUT COMMAND_STATUS *pCommandStatus, IN NVM_STATUS Status ) { NVDIMM_ENTRY(); if (pCommandStatus == NULL) { NVDIMM_DBG("pCommandStatus = NULL, Invalid parameter"); goto Finish; } pCommandStatus->GeneralStatus = Status; Finish: NVDIMM_EXIT(); } /** Search ObjectStatus from command status object list by specified Id and return pointer. @param[in] pCommandStatus - command status @param[in] ObjectId - Id for specified object @retval pointer to OBJECT_STATUS. @retval NULL if object with specified Id not found. **/ OBJECT_STATUS * GetObjectStatus( IN COMMAND_STATUS *pCommandStatus, IN UINT32 ObjectId ) { LIST_ENTRY *pObjectStatusNode = NULL; OBJECT_STATUS *pObjectStatus = NULL; OBJECT_STATUS *pSearchedObj = NULL; NVDIMM_ENTRY(); if (pCommandStatus == NULL) { goto Finish; } LIST_FOR_EACH(pObjectStatusNode, &pCommandStatus->ObjectStatusList) { pObjectStatus = OBJECT_STATUS_FROM_NODE(pObjectStatusNode); if (pObjectStatus == NULL) { goto Finish; } if (pObjectStatus->ObjectId == ObjectId) { pSearchedObj = pObjectStatus; break; } } Finish: NVDIMM_EXIT(); return pSearchedObj; } /** Erase all Nvm status codes @param[in/out] pObjectStatus pointer to object status (with Nvm Status bit field) **/ VOID EraseNvmStatus( IN OUT OBJECT_STATUS *pObjectStatus ) { UINT32 BitFieldCount = (NVM_LAST_STATUS_VALUE / 64) + 1; ZeroMem(pObjectStatus->StatusBitField.BitField, (sizeof(UINT64) * BitFieldCount)); } /** Erase status for specified ID in command status list (or create a new one with no status) @param[in, out] pCommandStatus - command status @param[in] ObjectId - Id for specified object @param[in] pObjectIdStr - Id for specified object as string representation, OPTIONAL @param[in] ObjectIdStrLength - Max length of pObjectIdStr, OPTIONAL @param[in] ObjectType - type for specified object **/ VOID EraseObjStatus( IN OUT COMMAND_STATUS *pCommandStatus, IN UINT32 ObjectId, IN CHAR16 *pObjectIdStr OPTIONAL, IN UINT32 ObjectIdStrLength OPTIONAL, IN OBJECT_TYPE ObjectType ) { OBJECT_STATUS *pObjectStatus = NULL; NVDIMM_ENTRY(); if (pCommandStatus == NULL) { NVDIMM_DBG("pCommandStatus = NULL, Invalid parameter"); goto Finish; } if (!IsListInitialized(pCommandStatus->ObjectStatusList)) { InitializeListHead(&pCommandStatus->ObjectStatusList); } pObjectStatus = GetObjectStatus(pCommandStatus, ObjectId); if (pObjectStatus != NULL) { EraseNvmStatus(pObjectStatus); goto Finish; } pObjectStatus = AllocateZeroPool(sizeof(*pObjectStatus)); if (pObjectStatus == NULL) { NVDIMM_ERR("Out of memory"); goto Finish; } pObjectStatus->ObjectId = ObjectId; pObjectStatus->ObjectType = ObjectType; if (pObjectIdStr != NULL && StrLen(pObjectIdStr) > 0) { pObjectStatus->IsObjectIdStr = TRUE; StrnCpyS(pObjectStatus->ObjectIdStr, MAX_OBJECT_ID_STR_LEN, pObjectIdStr, MIN(ObjectIdStrLength, MAX_OBJECT_ID_STR_LEN) - 1); } else { pObjectStatus->IsObjectIdStr = FALSE; } EraseNvmStatus(pObjectStatus); pObjectStatus->Signature = OBJECT_STATUS_SIGNATURE; pObjectStatus->Progress = 0; InitializeListHead(&pObjectStatus->ObjectStatusNode); InsertTailList(&pCommandStatus->ObjectStatusList, &pObjectStatus->ObjectStatusNode); pCommandStatus->ObjectStatusCount++; Finish: NVDIMM_EXIT(); } ipmctl-03.00.00.0485/DcpmPkg/common/NvmStatus.h000066400000000000000000000300441440615110200205460ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file NvmStatus.h * @brief Status Types for EFI_DCPMM_CONFIG2_PROTOCOL. */ #ifndef _NVM_STATUS_H_ #define _NVM_STATUS_H_ #include "NvmLimits.h" #include "NvmSharedDefs.h" #ifdef OS_BUILD #include #endif /** Return code status values as identified by #NvmStatusCode */ typedef INT16 NVM_STATUS; /** Return code status values identified by #DataStatusCode */ typedef INT16 DATA_STATUS; /** Structures for CLI command status output **/ /** Object type used by #COMMAND_STATUS **/ typedef enum { ObjectTypeSocket = 0, ///< Socket ObjectTypeDimm = 1, ///< PMem module ObjectTypeRegion = 2, ///< Region ObjectTypeNamespace = 3, ///< Namespace ObjectTypeUnknown = 4 ///< Unknown } OBJECT_TYPE; /** List head functions **/ /** #OBJECT_STATUS Signature */ #define OBJECT_STATUS_SIGNATURE SIGNATURE_64('O', 'B', 'J', 'S', 'T', 'A', 'T', 'S') /* Helper function to get #OBJECT_STATUS from a node */ #define OBJECT_STATUS_FROM_NODE(a) CR(a, OBJECT_STATUS, ObjectStatusNode, OBJECT_STATUS_SIGNATURE) /** Status bit field for #OBJECT_STATUS */ typedef struct _NVM_STATUS_BIT_FIELD { UINT64 BitField[(NVM_LAST_STATUS_VALUE / 64) + 1]; } NVM_STATUS_BIT_FIELD; /** Max length of the ObjectIdStr string */ #define MAX_OBJECT_ID_STR_LEN 30 #define MAX_STATUS_DETAILS_STR_LEN 64 /** Object status list structure **/ typedef struct { LIST_ENTRY ObjectStatusNode; ///< Object status node list pointer UINT64 Signature; ///< Signature must match #OBJECT_STATUS_SIGNATURE UINT32 ObjectId; ///< Object ID BOOLEAN IsObjectIdStr; ///< Is #ObjectIdStr valid? CHAR16 ObjectIdStr[MAX_OBJECT_ID_STR_LEN]; ///< String representation of Object ID NVM_STATUS_BIT_FIELD StatusBitField; ///< Bitfield for NVM Status UINT8 Progress; ///< Progress OBJECT_TYPE ObjectType; ///< Type of object } OBJECT_STATUS; /** COMMAND_STATUS type contains executed command status code as well as status codes for every Object (SOCKET, DIMM, NAMESPACE) which were involved in this operation **/ typedef struct { NVM_STATUS GeneralStatus; ///< General return status LIST_ENTRY ObjectStatusList; ///< List of #OBJECT_STATUS objects UINT16 ObjectStatusCount; ///< Count of object on #ObjectStatusList CHAR16 StatusDetails[MAX_STATUS_DETAILS_STR_LEN]; ///< Detailed return status, informs about data incorrectness } COMMAND_STATUS; /** Create command status as with specified command message. Function displays per PMem module status if such exists and summarizes status for the whole command. Memory allocated for status message and command status is freed after status is displayed. @param[in] HiiHandle handle to the HII database that contains NvmStatusStrings @param[in] pStatusMessage String with command information @param[in] pStatusPreposition String with preposition @param[in] pCommandStatus Command status data @param[in] ObjectIdNumberPreferred Use Object ID number if true, use Object ID string otherwise @param[in] DoNotPrintGeneralStatusSuccessCode If true, pCommandStatus->GeneralStatus is success, and there are no other nvm statuses set, then don't print out a success message. @param[out] ppOutputMessage buffer where output will be saved Warning: ppOutputMessage - should be freed in caller. @retval EFI_INVALID_PARAMETER pCommandStatus is NULL @retval EFI_SUCCESS All Ok **/ EFI_STATUS CreateCommandStatusString( IN EFI_HANDLE HiiHandle, IN CONST CHAR16 *pStatusMessage, IN CONST CHAR16 *pStatusPreposition, IN COMMAND_STATUS *pCommandStatus, IN BOOLEAN ObjectIdNumberPreferred, IN BOOLEAN DoNotPrintGeneralStatusSuccessCode, OUT CHAR16 **ppOutputMessage ); /** Disable previously saved status code @param[in] pObjectStatus pointer to object status (with Nvm Status bit field) @param[in] Code code to check if is set **/ VOID ClearNvmStatus( IN OBJECT_STATUS *pObjectStatus, IN NvmStatusCode Code ); /** Check if Object status got proper NVM status code set. @param[in, out] pObjectStatus pointer to object status (with Nvm Status bit field) @param[in] ObjectId - object for clearing status @param[in] Status Status code to check if is set @retval TRUE - if Object Status has got code set @retval FALSE - else **/ BOOLEAN IsSetNvmStatusForObject( IN OUT COMMAND_STATUS *pCommandStatus, IN UINT32 ObjectId, IN NVM_STATUS Status ); /** Check if Object status got proper NVM status code set. @param[in] pObjectStatus pointer to object status (with Nvm Status bit field) @param[in] Code code to check if is set @retval TRUE - if Object Status has got code set @retval FALSE - else **/ BOOLEAN IsSetNvmStatus( IN OBJECT_STATUS *pObjectStatus, IN NvmStatusCode Code ); /** Set proper code in Object status @param[in] pObjectStatus pointer to object status (with Nvm Status bit field) @param[in] Code code to set **/ VOID SetNvmStatus( IN OBJECT_STATUS *pObjectStatus, IN NvmStatusCode Code ); /** Search ObjectStatus from command status object list by specified Id and return pointer. @param[in] pCommandStatus - command status @param[in] ObjectId - Id for specified object @retval pointer to OBJECT_STATUS. @retval NULL if object with specified Id not found. **/ OBJECT_STATUS * GetObjectStatus( IN COMMAND_STATUS *pCommandStatus, IN UINT32 ObjectId ); /** Add (or update) status for specified ID in command status list @param[in, out] pCommandStatus - command status @param[in] ObjectId - Id for specified object @param[in] pObjectIdStr - Id for specified object as string representation, OPTIONAL @param[in] ObjectIdStrLength - Max length of pObjectIdStr, OPTIONAL @param[in] Status - status for update/set @param[in] ObjectType - type for specified object **/ VOID SetObjStatus( IN OUT COMMAND_STATUS *pCommandStatus, IN UINT32 ObjectId, IN CHAR16 *pObjectIdStr OPTIONAL, IN UINT32 ObjectIdStrLength OPTIONAL, IN NVM_STATUS Status, IN OBJECT_TYPE ObjectType ); /** Set progress for specified ID in command status list @param[in, out] pCommandStatus - command status @param[in] ObjectId - Id for specified object @param[in] Progress - progress to set **/ VOID SetObjProgress( IN OUT COMMAND_STATUS *pCommandStatus, IN UINT32 ObjectId, IN UINT8 Progress ); /** Translate all NVM Statuses of operation into Unicode string representing its brief description. @param[in] pObjectStatus - object containing lit NvmDimm status codes @param[in] pPrefixString - string prefix for each status @param[out] pppOutputLines - pointer for table of rows @param[out] pRowCount - number of rows in pppOutputLines @retval Pointer to a decoded string. It points to a static memory block, the caller should not change the returned buffer nor free it. **/ VOID GetAllNvmStatusCodeMessagesAsTableOfLines( IN OBJECT_STATUS *pObjectStatus, IN CONST CHAR16 *pPrefixString, OUT CHAR16 ***pppOutputLines, OUT UINT32 *pRowCount ); /** Translate all NVM Statuses of operation into Unicode string representing its brief description. @param[in] HiiHandle - Handle to the Hii Database that contains the NvmStatus strings @param[in] pObjectStatus - Object status containing BitField with NVM Statuses @param[in] pPrefixString - prefix for all lines of statuses @retval Pointer to a decoded string. Memory is dynamically allocated. It should be freed by caller. **/ CHAR16 * GetAllNvmStatusCodeMessages( IN EFI_HANDLE HiiHandle, IN OBJECT_STATUS *pObjectStatus, IN CONST CHAR16 *pPrefixString ); /** Clear Nvm status code for given object ID @param[in,out] pObjectStatus pointer to object status (with Nvm Status bit field) @param[in] ObjectId - object for checking status @param[in] Code code to clear @retval TRUE - if Object Status has got code set @retval FALSE - else **/ VOID ClearNvmStatusForObject( IN OUT COMMAND_STATUS *pCommandStatus, IN UINT32 ObjectId, IN NvmStatusCode Code ); /** Translate NVM operation return code into Unicode string representing its brief description. @param[in] HiiHandle EFI HANDLE to the HII database that contains NvmStatus strings @param[in] Code the status code returned from a NVM command. @retval Pointer to a decoded string. Memory is dynamically allocated. It should be freed by caller. **/ CHAR16 * GetSingleNvmStatusCodeMessage( IN EFI_HANDLE HiiHandle, IN NvmStatusCode Code ); /** Set general command status and zero status object counter @param[in, out] pCommandStatus - command status @param[in] Status - status for update/set **/ VOID ResetCmdStatus( IN OUT COMMAND_STATUS *pCommandStatus, IN NVM_STATUS Status ); /** Set general command status @param[in, out] pCommandStatus - command status @param[in] Status - status for update/set **/ VOID SetCmdStatus( IN OUT COMMAND_STATUS *pCommandStatus, IN NVM_STATUS Status ); #define NVM_ERROR(a) ((a) != NVM_SUCCESS && \ (a) != NVM_SUCCESS_FW_RESET_REQUIRED && \ (a) != NVM_SUCCESS_REQUIRES_POWER_CYCLE) #define THRESHOLD_UNDEFINED 0x7FFF // INT16 max positive value is treated as undefined value #define ENABLED_STATE_UNDEFINED 0xFFUL // UINT8 max value is treated as undefined value #define SENSOR_ID_UNDEFINED 0xFFUL /** FV return codes. **/ #define FW_SUCCESS 0x00 #define FW_INVALID_COMMAND_PARAMETER 0x01 #define FW_DATA_TRANSFER_ERROR 0x02 #define FW_INTERNAL_DEVICE_ERROR 0x03 #define FW_UNSUPPORTED_COMMAND 0x04 #define FW_DEVICE_BUSY 0x05 #define FW_INCORRECT_PASSPHRASE 0x06 #define FW_AUTH_FAILED 0x07 #define FW_INVALID_SECURITY_STATE 0x08 #define FW_SYSTEM_TIME_NOT_SET 0x09 #define FW_DATA_NOT_SET 0x0A #define FW_ABORTED 0x0B #define FW_REVISION_FAILURE 0x0D #define FW_INJECTION_NOT_ENABLED 0x0E #define FW_CONFIG_LOCKED 0x0F #define FW_INVALID_ALIGNMENT 0x10 #define FW_INCOMPATIBLE_DIMM_TYPE 0x11 #define FW_TIMEOUT_OCCURRED 0X12 #define FW_MEDIA_DISABLED 0x14 #define FW_UPDATE_ALREADY_OCCURRED 0x15 #define FW_NO_RESOURCES 0x16 #define FW_ERROR(A) (A != FW_SUCCESS) /** Initialize command status structure. Allocate memory and assign default values. @param[in] ppCommandStatus pointer to address of the structure @retval EFI_OUT_OF_RESOURCES Unable to allocate memory @retval EFI_SUCCESS All Ok **/ EFI_STATUS InitializeCommandStatus ( IN OUT COMMAND_STATUS **ppCommandStatus ); /** Free previously allocated and initialized command status structure @param[in] ppCommandStatus pointer to address of the structure **/ VOID FreeCommandStatus( IN COMMAND_STATUS **ppCommandStatus ); /** Erase all Nvm status codes @param[in,out] pObjectStatus pointer to object status (with Nvm Status bit field) **/ VOID EraseNvmStatus( IN OUT OBJECT_STATUS *pObjectStatus ); /** Erase status for specified ID in command status list (or create a new one with no status) @param[in, out] pCommandStatus - command status @param[in] ObjectId - Id for specified object @param[in] pObjectIdStr - Id for specified object as string representation, OPTIONAL @param[in] ObjectIdStrLength - Max length of pObjectIdStr, OPTIONAL @param[in] ObjectType - type for specified object **/ VOID EraseObjStatus( IN OUT COMMAND_STATUS *pCommandStatus, IN UINT32 ObjectId, IN CHAR16 *pObjectIdStr OPTIONAL, IN UINT32 ObjectIdStrLength OPTIONAL, IN OBJECT_TYPE ObjectType ); #endif /** _NVM_STATUS_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/NvmStatus.uni000066400000000000000000000737261440615110200211300ustar00rootroot00000000000000//// // Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause //// #langdef en "English" #string STR_DCPMM_DECIMAL_MARK #language en "." #string STR_DCPMM_COLON_MARK #language en ":" // Common strings: #string STR_EMPTY #language en "" #string STR_DCPMM_STATUS_SUCCESS #language en "Success" #string STR_DCPMM_STATUS_FAILED #language en "failed" //Object Types #string STR_DCPMM_STATUS_SOCKET #language en "Socket" #string STR_DCPMM_STATUS_PMM #language en "PMem module" #string STR_DCPMM_STATUS_NAMESPACE #language en "Namespace" #string STR_DCPMM_STATUS_REGION #language en "Region" //Error Levels #string STR_DCPMM_STATUS_ERROR #language en "Error" #string STR_DCPMM_STATUS_WARNING #language en "Warning" #string STR_DCPMM_STATUS_INFO #language en "Info" // #string STR_DCPMM_STATUS_EXECUTE_SUCCESS #language en "executed successfully" #string STR_ACPI_STATUS_NFIT #language en "NFIT" #string STR_ACPI_STATUS_PCAT #language en "PCAT" #string STR_ACPI_STATUS_PMTT #language en "PMTT" #string STR_ACPI_STATUS_UNKNOWN_TABLE #language en "Unknown Table" #string STR_ACPI_STATUS_ERR_INVALID_REVISION #language en "[%ls] Invalid ACPI table revision(0x%02x)" #string STR_ACPI_STATUS_ERR_INVALID_MAJOR_MINOR_REVISION #language en "[%ls] Invalid ACPI table revision(Major: 0x%x, Minor: 0x%x)" #string STR_DCPMM_STATUS_CONTROLLER_OUT_OF_RANGE #language en "Given controller temperature is out of range <%d,%d>" #string STR_DCPMM_STATUS_MEDIA_OUT_OF_RANGE #language en "Given media temperature is out of range <%d,%d>" #string STR_DCPMM_STATUS_CAPACITY_OUT_OF_RANGE #language en "Given capacity is out of range <%d,%d>" #string STR_DCPMM_STATUS_SUCCESS_FW_RESET_REQUIRED #language en "Success, a power cycle is required to activate the FW." #string STR_DCPMM_STATUS_ERR_OPERATION_NOT_STARTED #language en "Command not run" #string STR_DCPMM_STATUS_ERR_OPERATION_FAILED #language en "Command failed" #string STR_DCPMM_STATUS_ERR_INVALID_PARAMETER #language en "Invalid parameter" #string STR_DCPMM_STATUS_ERR_FORCE_REQUIRED #language en "This operation requires force option" #string STR_DCPMM_STATUS_ERR_DIMM_NOT_FOUND #language en "PMem module not found" #string STR_DCPMM_STATUS_ERR_MANAGEABLE_DIMM_NOT_FOUND #language en "No manageable PMem modules in the system." #string STR_DCPMM_STATUS_ERR_DIMM_EXCLUDED #language en "PMem module excluded as it is unmanageable, non-functional, or has a population issue." #string STR_DCPMM_STATUS_ERR_NO_USABLE_DIMMS #language en "No usable PMem modules due to all PMem modules being one of the following:\n- PMem modules unmanageable\n- PMem modules non-functional\n- PMem modules have a population issue" #string STR_DCPMM_STATUS_ERR_FIRMWARE_API_NOT_VALID #language en "The firmware image is not compatible with this version of software." #string STR_DCPMM_STATUS_ERR_FIRMWARE_DOWNGRADE #language en "Firmware unchanged." #string STR_DCPMM_STATUS_ERR_FIRMWARE_ALREADY_LOADED #language en "A firmware image is already staged for execution. A power cycle is required before another can be staged." #string STR_DCPMM_STATUS_ERR_FIRMWARE_FAILED_TO_STAGE #language en "Failed to validate and stage firmware image." #string STR_DCPMM_STATUS_ERR_DIMM_ID_DUPLICATED #language en "Duplicated DIMM ID" #string STR_DCPMM_STATUS_ERR_SOCKET_ID_NOT_VALID #language en "Invalid SocketID" #string STR_DCPMM_STATUS_ERR_SOCKET_ID_INCOMPATIBLE_W_DIMM_ID #language en "Specified SocketID and DimmID conflict" #string STR_DCPMM_STATUS_ERR_SOCKET_ID_DUPLICATED #language en "Duplicated SocketID" #string STR_DCPMM_STATUS_ERR_INVALID_PASSPHRASE #language en "Invalid passphrase" #string STR_DCPMM_STATUS_ERR_COMMAND_NOT_SUPPORTED_BY_THIS_SKU #language en "Command not supported by this SKU" #string STR_DCPMM_STATUS_ERR_CONFIG_NOT_SUPPORTED_BY_CURRENT_SKU #language en "Config not supported by current SKU" #string STR_DCPMM_STATUS_ERR_SECURITY_USER_PP_COUNT_EXPIRED #language en "The maximum user passphrase submission limit has been reached" #string STR_DCPMM_STATUS_ERR_SECURITY_MASTER_PP_COUNT_EXPIRED #language en "The maximum master passphrase submission limit has been reached" #string STR_DCPMM_STATUS_ERR_SPI_ACCESS_NOT_ENABLED #language en "PMem module SPI access is not enabled." #string STR_DCPMM_STATUS_ERR_FLASH_SPI_NO_LONGER_SUPPORTED #language en "PMem module FlashSPI recovery is no longer supported" #string STR_DCPMM_STATUS_ERR_SECURE_ERASE_NAMESPACE_EXISTS #language en "Namespaces exist on specified PMem module. They must be removed before an erase." #string STR_DCPMM_STATUS_ERR_PASSPHRASE_TOO_LONG #language en "Provided passphrase is too long" #string STR_DCPMM_STATUS_ERR_PASSPHRASE_NOT_PROVIDED #language en "Passphrase not provided" #string STR_DCPMM_STATUS_ERR_PASSPHRASES_DO_NOT_MATCH #language en "The new passphrase and the confirmed passphrase do not match" #string STR_DCPMM_STATUS_ERR_SENSOR_NOT_VALID #language en "Invalid sensor" #string STR_DCPMM_STATUS_ERR_SENSOR_ENABLED_STATE_INVALID_VALUE #language en "Invalid AlarmEnabled value" #string STR_DCPMM_STATUS_ERR_MEDIA_NOT_ACCESSIBLE #language en "Media not accessible" #string STR_DCPMM_STATUS_ERR_MEDIA_DISABLED_VALUE #language en "The media has been disabled" #string STR_DCPMM_STATUS_ERR_MEDIA_NOT_ACCESSIBLE_CANNOT_CONTINUE #language en "Media not accessible. Replace PMem module to continue" #string STR_DCPMM_STATUS_ERR_A_MEDIA_NOT_ACCESSIBLE_CANNOT_CONTINUE #language en "One or more PMem modules with media not accessible. Replace PMem module(s) to continue" #string STR_DCPMM_STATUS_ERR_AIT_DRAM_NOT_READY #language en "The AIT DRAM is not ready" #string STR_DCPMM_STATUS_ERR_MEDIA_INTERFACE_ENGINE_STALLED #language en "Firmware has stalled the media interface engine" #string STR_DCPMM_STATUS_ERR_ENABLE_SECURITY_NOT_ALLOWED #language en "Invalid request to enable security while goal creation is pending." #string STR_DCPMM_STATUS_ERR_CREATE_GOAL_NOT_ALLOWED #language en "Please set device security state to 'disabled' and try again." #string STR_DCPMM_STATUS_ERR_CREATE_GOAL_AUTO_PROV_ENABLED #language en "Automatic provisioning is enabled. Please disable to manually create goals" #string STR_DCPMM_STATUS_ERR_INVALID_SECURITY_STATE #language en "Command cannot be run in current security state." #string STR_DCPMM_STATUS_ERR_INVALID_SECURITY_OPERATION #language en "Unrecognizable security operation." #string STR_DCPMM_STATUS_ERR_UNABLE_TO_GET_SECURITY_STATE #language en "Unable to get security state" #string STR_DCPMM_STATUS_ERR_INCONSISTENT_SECURITY_STATE #language en "Inconsistent security state among PMem modules" #string STR_DCPMM_STATUS_WARN_2LM_MODE_OFF #language en "The requested goal will result in Memory Mode capacity that is unusable with the currently selected platform BIOS volatile mode." #string STR_DCPMM_STATUS_WARN_MM_PMM_DDR_NOT_PAIRED #language en "The requested goal will result in Memory Mode capacity that is unusable. An iMC has DDR without PMem module." #string STR_DCPMM_STATUS_WARN_PMEM_MODULE_NOT_PAIRED_FOR_2LM #language en "PMem module unusable for 2LM since it is not paired with a DDR on the iMC" #string STR_DCPMM_STATUS_WARN_NMFM_RATIO_LOWER_VIOLATION #language en "WARNING! The requested memory mode size is below the recommended NM:FM limit of 1:%ls" #string STR_DCPMM_STATUS_WARN_NMFM_RATIO_UPPER_VIOLATION #language en "WARNING! The requested memory mode size is above the recommended NM:FM limit of 1:%d" #string STR_DCPMM_STATUS_ERR_NMFM_RATIO_GREATER_THAN_ONE #language en "The requested memory mode size is below the NM:FM limit of 1:1" #string STR_DCPMM_STATUS_WARN_REDUCED_CAPACITY_DUE_TO_SKU #language en "The amount of mapped memory was limited based on the SKU resulting in un-mapped capacity." #string STR_DCPMM_STATUS_WARN_MAX_AD_PM_INTERLEAVE_SETS_EXCEEDED #language en "The platform is limited to %d interleave sets per Die, so only %d PMem modules will have AppDirect2 regions created." #string STR_DCPMM_STATUS_WARN_MAX_AD_NI_PM_INTERLEAVE_SETS_EXCEEDED #language en "The platform is limited to %d interleave sets per Die, so AppDirect capacities in %d PMem modules will be inaccessible." #string STR_DCPMM_STATUS_WARN_AD_NI_PM_INTERLEAVE_SETS_REDUCED #language en "Limit of %d interleave sets per Die exceeded for this platform. Reduced the number of AppDirect2 region(s)." #string STR_DCPMM_STATUS_ERR_MAX_PM_INTERLEAVE_SETS_EXCEEDED #language en "Limit of interleave sets per Die exceeded for this platform. Refer to limitation specified by 'MaxPMInterleaveSetsPerDie' value of the PCAT Platform Capability Info Table." #string STR_DCPMM_STATUS_ERR_NAMESPACE_TOO_SMALL_FOR_BTT #language en "The specified size is too small for an Optimized Namespace" #string STR_DCPMM_STATUS_ERR_DIMMS_CAPACITY_EXCEEDED #language en "PMem module capacity exceeded." #string STR_DCPMM_STATUS_ERR_PCD_BAD_DEVICE_CONFIG #language en "The configuration data is invalid or unrecognized." #string STR_DCPMM_STATUS_ERR_REGION_GOAL_CONF_AFFECTS_UNSPEC_DIMM #language en "Unspecified PMem module affected by region goal configuration." #string STR_DCPMM_STATUS_ERR_REGION_CURR_CONF_AFFECTS_UNSPEC_DIMM #language en "Unspecified PMem module affected by region current configuration." #string STR_DCPMM_STATUS_ERR_REGION_GOAL_CURR_CONF_AFFECTS_UNSPEC_DIMM #language en "Unspecified PMem module affected by region current and goal configuration." #string STR_DCPMM_STATUS_ERR_REGION_CONF_APPLYING_FAILED #language en "Device error - configuration cannot be sent to PMem module." #string STR_DCPMM_STATUS_ERR_REGION_CONF_UNSUPPORTED_CONFIG #language en "Requests using -dimm must conform to ONE of:\n- Target all PMem modules on a given socket\n- Target all unconfigured PMem modules\n- Target PMem modules for 100%% MemoryMode with all unspecified PMem modules configured for MemoryMode only\n- Target PMem modules for AppDirect Not-Interleaved with all unspecified PMem modules configured for AppDirect Not-Interleaved only" #string STR_DCPMM_STATUS_ERR_REGION_NOT_FOUND #language en "Region not found" #string STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_MANAGEMENT_SOFT #language en "Platform does not support changing configuration through management software" #string STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_2LM_MODE #language en "Platform does not support Memory Mode" #string STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_MIXED_MODE #language en "Platform does not support Mixed Mode" #string STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_PM_MODE #language en "Platform does not support App Direct Mode" #string STR_DCPMM_STATUS_ERR_REGION_CURR_CONF_EXISTS #language en "PMem modules on one or more sockets already have a configured goal. Delete this existing goal before creating a new one." #string STR_DCPMM_STATUS_ERR_REGION_SIZE_TOO_SMALL_FOR_INT_SET_ALIGNMENT #language en "The region size is too small for platform alignment" #string STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_SPECIFIED_INT_SIZES #language en "Platform does not support specified interleave iMC and Channel sizes." #string STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_DEFAULT_INT_SIZES #language en "Platform does not support default interleave iMC and Channel sizes for specified configuration." #string STR_DCPMM_STATUS_ERR_REGION_NOT_HEALTHY #language en "Region is not healthy" #string STR_DCPMM_STATUS_ERR_REGION_NOT_ENOUGH_SPACE_FOR_PM_NAMESPACE #language en "Not enough space to create App Direct Namespace." #string STR_DCPMM_STATUS_ERR_REGION_NO_GOAL_EXISTS_ON_DIMM #language en "There is no goal configuration on this PMem module." #string STR_DCPMM_STATUS_ERR_RESERVE_DIMM_REQUIRES_AT_LEAST_TWO_DIMMS #language en "At least 2 PMem modules are required to use reserve option." #string STR_DCPMM_STATUS_ERR_REGION_GOAL_NAMESPACE_EXISTS #language en "Namespaces exist on specified PMem modules. They have to be removed before running this command.\nWARNING: Removing Namespaces will erase existing data. Please make a backup first." #string STR_DCPMM_STATUS_ERR_REGION_REMAINING_SIZE_NOT_IN_LAST_PROPERTY #language en "Remaining value can be used only for one property and only on the last App Direct Size property" #string STR_DCPMM_STATUS_ERR_PERS_MEM_MUST_BE_APPLIED_TO_ALL_DIMMS #language en "Unable to find a valid mapping for the requested configuration." #string STR_DCPMM_STATUS_ERR_CREATE_NAMESPACE_NOT_ALLOWED #language en "Pending goal request found on PMem modules that are part of this namespace request. Please reboot to apply the goal or delete the goal and try again." #string STR_DCPMM_STATUS_ERR_PCD_CURR_CONF_MISSING #language en "One or more PMem modules have invalid PCD data. A new goal (create -f -goal) is recommended to restore valid PCD data, then try again." #string STR_DCPMM_STATUS_ERR_OPEN_FILE_WITH_WRITE_MODE_FAILED #language en "Open or create the file with write mode failed." #string STR_DCPMM_STATUS_ERR_DUMP_NO_CONFIGURED_DIMMS #language en "There are no configured PMem modules to dump." #string STR_DCPMM_STATUS_ERR_DUMP_FILE_OPERATION_FAILED #language en "File operation failed." #string STR_DCPMM_STATUS_ERR_LOAD_VERSION #language en "Incorrect load file version" #string STR_DCPMM_STATUS_ERR_LOAD_INVALID_DATA_IN_FILE #language en "Invalid file." #string STR_DCPMM_STATUS_ERR_LOAD_IMPROPER_CONFIG_IN_FILE #language en "Improper configuration." #string STR_DCPMM_STATUS_ERR_LOAD_DIMM_COUNT_MISMATCH #language en "Incomplete configuration in the file." #string STR_DCPMM_STATUS_ERR_IMAGE_EXAMINE_INVALID #language en "Invalid" #string STR_DCPMM_STATUS_SUCCESS_IMAGE_EXAMINE_OK #language en "Valid" #string STR_DCPMM_STATUS_ERR_IMAGE_EXAMINE_LOWER_VERSION #language en "Downgrade (with confirmation or the force option)" #string STR_DCPMM_STATUS_ERR_IMAGE_FILE_NOT_VALID #language en "Image file is not valid" #string STR_DCPMM_STATUS_ERR_DIMM_SKU_PACKAGE_SPARING_MISMATCH #language en "Inconsistent Package Sparing capabilities of the PMem modules" #string STR_DCPMM_STATUS_ERR_DIMM_SKU_MODE_MISMATCH #language en "Inconsistent Mode capabilities of the PMem modules" #string STR_DCPMM_STATUS_ERR_DIMM_SKU_SECURITY_MISMATCH #language en "Inconsistent Security capabilities of the PMem modules" #string STR_DCPMM_STATUS_ERR_OPERATION_NOT_SUPPORTED_BY_MIXED_SKU #language en "Mixed PMem module SKUs detected. The command is not supported in the current context." #string STR_DCPMM_STATUS_ERR_INCOMPATIBLE_SKU_ON_MODULE #language en "This module's SKU is incompatible with the SKU(s) of other installed module(s)." #string STR_DCPMM_STATUS_ERR_INJECTION_BIOS_KNOB_NOT_ENABLED #language en "The BIOS error injection knob is not enabled. Please enable and try again." #string STR_DCPMM_STATUS_SUCCESS_REQUIRES_POWER_CYCLE #language en "Success. A power cycle is required for all changes to take effect." #string STR_DCPMM_STATUS_ERR_NONE_DIMM_FULFILLS_CRITERIA #language en "No PMem module in the region fulfills requested properties." #string STR_DCPMM_STATUS_ERR_NONE_ISET_FULFILLS_CRITERIA #language en "App Direct Namespace with requested properties can't be created on the Region." #string STR_DCPMM_STATUS_ERR_UNSUPPORTED_BLOCK_SIZE #language en "Unsupported BlockSize specified" #string STR_DCPMM_STATUS_ERR_INVALID_NAMESPACE_TYPE #language en "Unable to determine Namespace type" #string STR_DCPMM_STATUS_ERR_INVALID_NAMESPACE_CAPACITY #language en "Requested capacity is less than minimum namespace size" #string STR_DCPMM_STATUS_ERR_NAMESPACE_DOES_NOT_EXIST #language en "Namespace doesn't exist" #string STR_DCPMM_STATUS_ERR_NAMESPACE_CONFIGURATION_BROKEN #language en "Namespace configuration is broken" #string STR_DCPMM_STATUS_ERR_NOT_ENOUGH_FREE_SPACE #language en "No free space to complete the operation." #string STR_DCPMM_STATUS_ERR_NOT_ENOUGH_FREE_SPACE_BTT #language en "Not enough free space. Optimized Namespaces require more blocks than requested." #string STR_DCPMM_STATUS_ERR_FAILED_TO_UPDATE_BTT #language en "Failed to clean an Optimized Namespace. Please delete and recreate it manually." #string STR_DCPMM_STATUS_ERR_PLATFORM_NOT_SUPPORT_BLOCK_MODE #language en "Platform does not support Storage Mode" #string STR_DCPMM_STATUS_WARN_BLOCK_MODE_DISABLED #language en "Namespace created but not enabled, the block access is disabled on the PMem module" #string STR_DCPMM_STATUS_ERR_BADALIGNMENT #language en "The specified size does not have the required alignment." #string STR_DCPMM_STATUS_ERR_RENAME_NAMESPACE_NOT_SUPPORTED #language en "Renaming of a namespace is not supported." #string STR_DCPMM_STATUS_ERR_FAILED_TO_INIT_NS_LABELS #language en "Unable to initialize namespace labels." #string STR_DCPMM_STATUS_ERR_SMART_FAILED_TO_GET_SMART_INFO #language en "Failed to fetch PMem module SMART Info" #string STR_DCPMM_STATUS_WARN_SMART_NONCRITICAL_HEALTH_ISSUE #language en "Non-critical health state, maintenance required" #string STR_DCPMM_STATUS_ERR_SMART_CRITICAL_HEALTH_ISSUE #language en "Critical health state, features or performance degraded" #string STR_DCPMM_STATUS_ERR_SMART_FATAL_HEALTH_ISSUE #language en "Fatal health state, data loss is imminent" #string STR_DCPMM_STATUS_ERR_SMART_READ_ONLY_HEALTH_ISSUE #language en "Read-only health state" #string STR_DCPMM_STATUS_ERR_SMART_UNKNOWN_HEALTH_ISSUE #language en "Unknown health state" #string STR_DCPMM_STATUS_ERR_FAILED_TO_GET_DIMM_INFO #language en "Failed to get the PMem module info" #string STR_DCPMM_STATUS_ERR_FW_SET_OPTIONAL_DATA_POLICY_FAILED #language en "Failed to set Optional Configuration Data Policy" #string STR_DCPMM_STATUS_ERR_INVALID_OPTIONAL_DATA_POLICY_STATE #language en "Invalid Optional Configuration Data Policy" #string STR_DCPMM_STATUS_ERR_FAILED_TO_GET_DIMM_REGISTERS #language en "Failed to get PMem module registers" #string STR_DCPMM_STATUS_ERR_SMBIOS_DIMM_ENTRY_NOT_FOUND_IN_NFIT #language en "Uninitialized PMem module found in SMBIOS" #string STR_DCPMM_STATUS_OPERATION_IN_PROGRESS #language en "Operation in progress" #string STR_DCPMM_STATUS_ERR_GET_PCD_FAILED #language en "Get Platform Config Data failed" #string STR_DCPMM_STATUS_ERR_ARS_IN_PROGRESS #language en "An address range scrub operation is currently in progress. Please try again after a few minutes." #string STR_DCPMM_STATUS_ERR_FWUPDATE_IN_PROGRESS #language en "A firmware update operation is currently in progress" #string STR_DCPMM_STATUS_ERR_OVERWRITE_DIMM_IN_PROGRESS #language en "An overwrite PMem module operation is currently in progress" #string STR_DCPMM_STATUS_ERR_UNKNOWN_LONG_OP_IN_PROGRESS #language en "An unknown long operation is currently in progress" #string STR_DCPMM_STATUS_ERR_LONG_OP_ABORTED_OR_REVISION_FAILURE #language en "The long operation was aborted or FW reversion failure" #string STR_DCPMM_STATUS_ERR_FW_UPDATE_AUTH_FAILURE #language en "FW Update authentication failure" #string STR_DCPMM_STATUS_ERR_UNSUPPORTED_COMMAND #language en "Unsupported command" #string STR_DCPMM_STATUS_ERR_DEVICE_ERROR #language en "Device error" #string STR_DCPMM_STATUS_ERR_TRANSFER_ERROR #language en "Data transfer error" #string STR_DCPMM_STATUS_ERR_UNABLE_TO_STAGE_NO_LONGOP #language en "FW image rejected during staging. Rerun FW update for rejection reason" #string STR_DCPMM_STATUS_ERR_LONG_OP_UNKNOWN #language en "The long operation complete code is unknown" #string STR_DCPMM_STATUS_ERR_APPDIRECT_IN_SYSTEM #language en "AppDirect settings apply system-wide and capacity already exists" #string STR_DCPMM_STATUS_DEFAULT #language en "Other error" #string STR_DCPMM_SECSTATE_UNKNOWN #language en "Unknown" #string STR_DCPMM_SECSTATE_DISABLED #language en "Disabled" #string STR_DCPMM_SECSTATE_FROZEN #language en "Frozen" #string STR_DCPMM_SECSTATE_UNLOCKED #language en "Unlocked" #string STR_DCPMM_SECSTATE_MASTER_PW_MAX #language en "MP Exceeded" #string STR_DCPMM_SECSTATE_LOCKED #language en "Locked" #string STR_DCPMM_SECSTATE_NOT_SUPPORTED #language en "Not Supported" #string STR_DCPMM_SECSTATE_PW_MAX #language en "Exceeded" #string STR_DCPMM_SEC_OPTIN_SVN_DOWNGRADE_DISABLED #language en "Disabled" #string STR_DCPMM_SEC_OPTIN_SVN_DOWNGRADE_ENABLED #language en "Enabled" #string STR_DCPMM_SEC_OPTIN_SECURE_ERASE_NO_MASTER_PASSPHRASE #language en "No Master Passphrase" #string STR_DCPMM_SEC_OPTIN_SECURE_ERASE_MASTER_PASSPHRASE_ENABLED #language en "Master Passphrase Enabled" #string STR_DCPMM_SEC_OPTIN_SECURE_S3 #language en "SecureS3" #string STR_DCPMM_SEC_OPTIN_UNSECURE_S3 #language en "UnsecureS3" #string STR_DCPMM_SEC_OPTIN_FW_ACTIVATE_DISABLED #language en "Disabled" #string STR_DCPMM_SEC_OPTIN_FW_ACTIVATE_ENABLED #language en "Enabled" #string STR_DCPMM_SEC_OPTIN_UNKNOWN #language en "Unknown" #string STR_DCPMM_BOOT_STATUS_INTERFACE_UNKNOWN #language en "DDRT/SMBUS Status Unknown" #string STR_DCPMM_BOOT_STATUS_BSR_UNKNOWN #language en "BSR Unknown" #string STR_DCPMM_BOOT_STATUS_SUCCESS #language en "Success" #string STR_DCPMM_BOOT_STATUS_MEDIA_NOT_READY #language en "Media Not Ready" #string STR_DCPMM_BOOT_STATUS_MEDIA_ERROR #language en "Media Error" #string STR_DCPMM_BOOT_STATUS_MEDIA_DISABLED #language en "Media Disabled" #string STR_DCPMM_BOOT_STATUS_DDRT_NOT_READY #language en "DDRT Not Ready" #string STR_DCPMM_BOOT_STATUS_SMBUS_NOT_READY #language en "SMBUS Not Ready" #string STR_DCPMM_BOOT_STATUS_MAILBOX_NOT_READY #language en "Mailbox Not Ready" #string STR_DCPMM_BOOT_STATUS_RR #language en "Reboot Required" #string STR_DCPMM_VIEW_PMM_FORM_ARSSTATUS #language en "ARS status" #string STR_DCPMM_VIEW_PMM_FORM_OVERWRITE_DIMM_STATUS #language en "Overwrite PMem module status" #string STR_DCPMM_LONG_OP_STATUS_UNKNOWN #language en "Unknown" #string STR_DCPMM_LONG_OP_STATUS_NOT_STARTED #language en "Not started" #string STR_DCPMM_LONG_OP_STATUS_IN_PROGRESS #language en "In progress" #string STR_DCPMM_LONG_OP_STATUS_COMPLETED #language en "Completed" #string STR_DCPMM_LONG_OP_STATUS_ABORTED #language en "Aborted" #string STR_DCPMM_LONG_OP_STATUS_ERROR #language en "Error" #string STR_DCPMM_FW_UPDATE_STATUS_STAGED #language en "Staged successfully" #string STR_DCPMM_FW_UPDATE_STATUS_SUCCESS #language en "Update loaded successfully" #string STR_DCPMM_FW_UPDATE_STATUS_FAIL #language en "Update failed to load, fell back to previous firmware" #string STR_DCPMM_FW_UPDATE_STATUS_UNKNOWN #language en "Unknown" #string STR_DCPMM_QUIESCE_REQUIRED #language en "Required" #string STR_DCPMM_QUIESCE_NOT_REQUIRED #language en "Not required" #string STR_DCPMM_STAGED_FW_NOT_ACTIVATABLE #language en "Not activatable, reboot is required" #string STR_DCPMM_STAGED_FW_ACTIVATABLE #language en "Activatable" #string STR_DCPMM_VIEW_FIRMWARE_VERSION_NOT_VALID #language en "Incorrect FW version" #string STR_DCPMM_VIEW_FIRMWARE_INCOMPATIBLE_TO_CTLR_STEPPING #language en "Image not compatible with this PMem module" #string STR_DCPMM_PROVISIONING_FORM_GOAL_STATUS_UNKNOWN #language en "Unknown: The status cannot be determined." #string STR_DCPMM_PROVISIONING_FORM_GOAL_STATUS_REBOOT_REQUIRED #language en "New: A reboot is required for the memory allocation goal to be processed by the BIOS." #string STR_DCPMM_PROVISIONING_FORM_GOAL_STATUS_INVALID_GOAL #language en "Failed - Bad request: The BIOS failed to process the memory allocation goal because it was invalid." #string STR_DCPMM_PROVISIONING_FORM_GOAL_STATUS_NOT_ENOUGH_RESOURCES #language en "Failed - Not enough resources: There were not enough resources for the BIOS to process the memory allocation goal." #string STR_DCPMM_PROVISIONING_FORM_GOAL_STATUS_FIRMWARE_ERROR #language en "Failed - Firmware error: The BIOS failed to process the memory allocation goal due to a firmware error." #string STR_DCPMM_PROVISIONING_FORM_GOAL_STATUS_UNKNOWN_ERROR #language en "Failed - Unknown: The BIOS failed to process the memory allocation goal due to an unknown error." #string STR_DCPMM_STATUS_ERR_FW_DBG_LOG_FAILED_TO_GET_SIZE #language en "Failed to get firmware debug log size" #string STR_DCPMM_STATUS_ERR_FW_SET_LOG_LEVEL_FAILED #language en "Failed to set firmware log level" #string STR_DCPMM_STATUS_INFO_FW_DBG_LOG_NO_LOGS_TO_FETCH #language en "No firmware debug logs to fetch" #string STR_DCPMM_STATUS_ERR_API_NOT_SUPPORTED #language en "API not supported" #string STR_DCPMM_STATUS_ERR_PCD_DELETE_DENIED #language en "PCD Deletion Denied" #string STR_DCPMM_STATUS_ERR_UNKNOWN #language en "Unknown" #string STR_DCPMM_STATUS_ERR_INVALID_PERMISSIONS #language en "Invalid permissions" #string STR_DCPMM_STATUS_ERR_BAD_DEVICE #language en "Bad device" #string STR_DCPMM_STATUS_ERR_BUSY_DEVICE #language en "One or more PMem modules are busy." #string STR_DCPMM_STATUS_ERR_GENERAL_OS_DRIVER_FAILURE #language en "OS driver failure" #string STR_DCPMM_STATUS_ERR_NO_MEM #language en "No memory" #string STR_DCPMM_STATUS_ERR_BAD_SIZE #language en "Bad size" #string STR_DCPMM_STATUS_ERR_TIMEOUT #language en "Timeout" #string STR_DCPMM_STATUS_ERR_DATA_TRANSFER #language en "Data transfer error" #string STR_DCPMM_STATUS_ERR_GENERAL_DEV_FAILURE #language en "Device failure" #string STR_DCPMM_STATUS_ERR_BAD_FW #language en "Invalid firmware" #string STR_DCPMM_STATUS_ERR_DRIVERFAILED #language en "Driver failure" #string STR_DCPMM_STATUS_ERR_NOT_SUPPORTED #language en "Unsupported" #string STR_DCPMM_STATUS_ERR_SPD_NOT_ACCESSIBLE #language en "Module SPD not accessible" #string STR_DCPMM_STATUS_ERR_INCOMPATIBLE_HARDWARE_REVISION #language en "Incompatible hardware revision" #string STR_DCPMM_VIEW_DCPMM_FORM_HEALTH_REASON #language en "Health state reason" #string STR_DCPMM_VIEW_DCPMM_FORM_PERCENTAGE_REMAINING #language en "Percentage remaining less than 1%" #string STR_DCPMM_VIEW_DCPMM_PACKAGE_SPARING_HAPPENED #language en "Package Sparing occurred" #string STR_DCPMM_VIEW_DCPMM_FORM_CAP_SELF_TEST_WARNING #language en "CAP Self-Test warning" #string STR_DCPMM_VIEW_DCPMM_FORM_PERCENTAGE_REMAINING_ZERO #language en "Percentage remaining is 0" #string STR_DCPMM_VIEW_DCPMM_FORM_DIE_FAILURE #language en "Die Failure" #string STR_DCPMM_VIEW_DCPMM_FORM_AIT_DRAM_DISABLED #language en "AIT DRAM disabled" #string STR_DCPMM_VIEW_DCPMM_FORM_CAP_SELF_TEST_FAIL #language en "CAP Self-Test fail" #string STR_DCPMM_VIEW_DCPMM_FORM_CRITICAL_INTERNAL_FAILURE #language en "Critical internal failure" #string STR_DCPMM_VIEW_DCPMM_FORM_PERFORMANCE_DEGRADED #language en "Performance degraded" #string STR_DCPMM_VIEW_DCPMM_FORM_CAP_SELF_TEST_COMM_FAILURE #language en "CAP Self-Test communication failure" #string STR_DCPMM_VIEW_DCPMM_FORM_NONE #language en "None" #string STR_HEALTH_STATE #language en "Health state" #string STR_HEALTHY #language en "Healthy" #string STR_NON_CRITICAL_FAILURE #language en "Noncritical failure" #string STR_CRITICAL_FAILURE #language en "Critical failure" #string STR_FATAL_FAILURE #language en "Fatal failure" #string STR_UNMANAGEABLE #language en "Unmanageable" #string STR_NON_FUNCTIONAL #language en "Non-functional" #string STR_UNKNOWN #language en "Unknown" #string STR_DCPMM_STATUS_SUCCESS_NO_EVENT_FOUND #language en "Event log empty" #string STR_DCPMM_STATUS_ERR_FILE_NOT_FOUND #language en "Cannot open file or file not found" #string STR_DCPMM_CAPACITY_UNIT_B #language en "B" #string STR_DCPMM_CAPACITY_UNIT_MB #language en "MB" #string STR_DCPMM_CAPACITY_UNIT_MIB #language en "MiB" #string STR_DCPMM_CAPACITY_UNIT_GB #language en "GB" #string STR_DCPMM_CAPACITY_UNIT_GIB #language en "GiB" #string STR_DCPMM_CAPACITY_UNIT_TB #language en "TB" #string STR_DCPMM_CAPACITY_UNIT_TIB #language en "TiB" #string STR_DPCPMM_MSG_VERB_DELETE #language en "Delete" #string STR_DPCPMM_MSG_VERB_DELETING #language en "Deleting" #string STR_DPCPMM_MSG_VERB_SKIPPED_DELETING #language en "Skipped deleting" #string STR_DCPMM_RESTRICTION_NONE #language en "None" #string STR_DCPMM_RESTRICTION_BIOS_ONLY #language en "BIOS only" #string STR_DCPMM_RESTRICTION_SMBUS_ONLY #language en "SMBus only" #string STR_DCPMM_RESTRICTION_BIOS_SMBUS_ONLY #language en "BIOS and SMBus only" #string STR_DCPMM_RESTRICTION_MGMT_ONLY #language en "Management only" #string STR_DCPMM_RESTRICTION_MGMT_BIOS_ONLY #language en "Management and BIOS only" #string STR_DCPMM_RESTRICTION_MGMT_SMBUS_ONLY #language en "Management and SMBus only" #string STR_DCPMM_RESTRICTION_MGMT_BIOS_SMBUS_ONLY #language en "Management, BIOS and SMBus only" #string STR_DCPMM_RESTRICTION_UNSUPPORTED #language en "Unsupported" #string STR_DCPMM_RESTRICTION_INVALID #language en "" #string STR_DCPMM_DIMM_HEALTHY_FW_NOT_RECOVERABLE #language en "Full SPI flash recovery of FW on a healthy PMem module is not supported" #string STR_DCPMM_STATUS_ERR_MASTER_PASSPHRASE_NOT_SET #language en "Master Passphrase is enabled but not set." #string STR_DCPMM_STATUS_ERR_INCOMPATIBLE_SOFTWARE_REVISION #language en "This version of ipmctl is incompatible with the UEFI platform firmware. Please upgrade to a newer version of ipmctl." #string STR_DCPMM_STATUS_ERR_INITIALIZATION_FAILED_NO_MODULES #language en "Initialization failed. No PMem modules in the system." #string STR_DCPMM_STATUS_WARN_PMTT_TABLE_NOT_FOUND #language en "PMTT table is not found. BIOS might reject goal request upon reboot for SKU limit or NM:FM violation" #string STR_DCPMM_FIPS_MODE_STATUS_NON_FIPS_MODE #language en "Non-FIPS mode" #string STR_DCPMM_FIPS_MODE_STATUS_NON_FIPS_MODE_UNTIL_NEXT_BOOT #language en "Non-FIPS mode, but will transition to FIPS mode on next boot" #string STR_DCPMM_FIPS_MODE_STATUS_INITIALIZATION_NOT_DONE #language en "FIPS mode, one-time initialization not done" #string STR_DCPMM_FIPS_MODE_STATUS_INITIALIZATION_DONE #language en "FIPS mode, one-time initialization done"ipmctl-03.00.00.0485/DcpmPkg/common/NvmTables.c000066400000000000000000000162221440615110200204720ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "NvmTables.h" #include #include #include /** Frees the memory of parsed Nfit subtables. @param[in] ParsedNfit pointer to the NFit header. **/ STATIC VOID FreeNfitSubTables( IN ParsedFitHeader *pParsedNfit ); /** Frees the memory associated in the parsed PCAT table. @param[in, out] pParsedPcat pointer to the PCAT header. **/ VOID FreeParsedPcat( IN OUT ParsedPcatHeader **ppParsedPcat ) { UINT32 Index = 0; ACPI_REVISION Revision; ParsedPcatHeader * pParsedPcat = NULL; if (ppParsedPcat == NULL || *ppParsedPcat == NULL) { return; } pParsedPcat = *ppParsedPcat; Revision = pParsedPcat->pPlatformConfigAttr->Header.Revision; FREE_POOL_SAFE(pParsedPcat->pPlatformConfigAttr); if (IS_ACPI_REV_MAJ_0_MIN_VALID(Revision)) { for (Index = 0; Index < pParsedPcat->PlatformCapabilityInfoNum; Index++) { FREE_POOL_SAFE(pParsedPcat->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo[Index]); } FREE_POOL_SAFE(pParsedPcat->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo); } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(Revision)) { for (Index = 0; Index < pParsedPcat->PlatformCapabilityInfoNum; Index++) { FREE_POOL_SAFE(pParsedPcat->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[Index]); } FREE_POOL_SAFE(pParsedPcat->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo); } if (IS_ACPI_REV_MAJ_0_MIN_VALID(Revision)) { for (Index = 0; Index < pParsedPcat->MemoryInterleaveCapabilityInfoNum; Index++) { FREE_POOL_SAFE(pParsedPcat->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo[Index]); } FREE_POOL_SAFE(pParsedPcat->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo); } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(Revision)) { for (Index = 0; Index < pParsedPcat->MemoryInterleaveCapabilityInfoNum; Index++) { FREE_POOL_SAFE(pParsedPcat->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo[Index]); } FREE_POOL_SAFE(pParsedPcat->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo); } for (Index = 0; Index < pParsedPcat->RuntimeInterfaceValConfInputNum; Index++) { FREE_POOL_SAFE(pParsedPcat->ppRuntimeInterfaceValConfInput[Index]); } FREE_POOL_SAFE(pParsedPcat->ppRuntimeInterfaceValConfInput); for (Index = 0; Index < pParsedPcat->ConfigManagementAttributesInfoNum; Index++) { FREE_POOL_SAFE(pParsedPcat->ppConfigManagementAttributesInfo[Index]); } FREE_POOL_SAFE(pParsedPcat->ppConfigManagementAttributesInfo); if (IS_ACPI_REV_MAJ_0_MIN_VALID(Revision)) { for (Index = 0; Index < pParsedPcat->SocketSkuInfoNum; Index++) { FREE_POOL_SAFE(pParsedPcat->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable[Index]); } FREE_POOL_SAFE(pParsedPcat->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable); } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(Revision)) { for (Index = 0; Index < pParsedPcat->SocketSkuInfoNum; Index++) { FREE_POOL_SAFE(pParsedPcat->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index]); } FREE_POOL_SAFE(pParsedPcat->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable); } FREE_POOL_SAFE(*ppParsedPcat); } /** Frees the memory associated in the parsed PMTT table. @param[in, out] pParsedPmtt pointer to the PMTT header. **/ VOID FreeParsedPmtt( IN OUT ParsedPmttHeader **ppParsedPmtt ) { UINT32 Index = 0; ParsedPmttHeader * pParsedPmtt = NULL; if (ppParsedPmtt == NULL || *ppParsedPmtt == NULL) { return; } pParsedPmtt = *ppParsedPmtt; FREE_POOL_SAFE(pParsedPmtt->pPmtt); for (Index = 0; Index < pParsedPmtt->SocketsNum; Index++) { FREE_POOL_SAFE(pParsedPmtt->ppSockets[Index]); } FREE_POOL_SAFE(pParsedPmtt->ppSockets); for (Index = 0; Index < pParsedPmtt->DiesNum; Index++) { FREE_POOL_SAFE(pParsedPmtt->ppDies[Index]); } FREE_POOL_SAFE(pParsedPmtt->ppDies); for (Index = 0; Index < pParsedPmtt->iMCsNum; Index++) { FREE_POOL_SAFE(pParsedPmtt->ppiMCs[Index]); } FREE_POOL_SAFE(pParsedPmtt->ppiMCs); for (Index = 0; Index < pParsedPmtt->ChannelsNum; Index++) { FREE_POOL_SAFE(pParsedPmtt->ppChannels[Index]); } FREE_POOL_SAFE(pParsedPmtt->ppChannels); for (Index = 0; Index < pParsedPmtt->SlotsNum; Index++) { FREE_POOL_SAFE(pParsedPmtt->ppSlots[Index]); } FREE_POOL_SAFE(pParsedPmtt->ppSlots); for (Index = 0; Index < pParsedPmtt->DDRModulesNum; Index++) { FREE_POOL_SAFE(pParsedPmtt->ppDDRModules[Index]); } FREE_POOL_SAFE(pParsedPmtt->ppDDRModules); for (Index = 0; Index < pParsedPmtt->DCPMModulesNum; Index++) { FREE_POOL_SAFE(pParsedPmtt->ppDCPMModules[Index]); } FREE_POOL_SAFE(pParsedPmtt->ppDCPMModules); FREE_POOL_SAFE(*ppParsedPmtt); } /** Frees the memory of parsed Nfit subtables. @param[in] ParsedNfit pointer to the NFit header. **/ STATIC VOID FreeNfitSubTables( IN ParsedFitHeader *ParsedNfit ) { UINT32 Index = 0; if (ParsedNfit == NULL) { return; } for(Index = 0; Index < ParsedNfit->BWRegionTblesNum; Index++) { FREE_POOL_SAFE(ParsedNfit->ppBWRegionTbles[Index]); } FREE_POOL_SAFE(ParsedNfit->ppBWRegionTbles); ParsedNfit->BWRegionTblesNum = 0; for(Index = 0; Index < ParsedNfit->ControlRegionTblesNum; Index++) { FREE_POOL_SAFE(ParsedNfit->ppControlRegionTbles[Index]); } FREE_POOL_SAFE(ParsedNfit->ppControlRegionTbles); ParsedNfit->ControlRegionTblesNum = 0; for(Index = 0; Index < ParsedNfit->FlushHintTblesNum; Index++) { FREE_POOL_SAFE(ParsedNfit->ppFlushHintTbles[Index]); } FREE_POOL_SAFE(ParsedNfit->ppFlushHintTbles); ParsedNfit->FlushHintTblesNum = 0; for(Index = 0; Index < ParsedNfit->InterleaveTblesNum; Index++) { FREE_POOL_SAFE(ParsedNfit->ppInterleaveTbles[Index]); } FREE_POOL_SAFE(ParsedNfit->ppInterleaveTbles); ParsedNfit->InterleaveTblesNum = 0; for(Index = 0; Index < ParsedNfit->NvDimmRegionMappingStructuresNum; Index++) { FREE_POOL_SAFE(ParsedNfit->ppNvDimmRegionMappingStructures[Index]); } FREE_POOL_SAFE(ParsedNfit->ppNvDimmRegionMappingStructures); ParsedNfit->NvDimmRegionMappingStructuresNum = 0; for(Index = 0; Index < ParsedNfit->SmbiosTblesNum; Index++) { FREE_POOL_SAFE(ParsedNfit->ppSmbiosTbles[Index]); } FREE_POOL_SAFE(ParsedNfit->ppSmbiosTbles); ParsedNfit->SmbiosTblesNum = 0; for(Index = 0; Index < ParsedNfit->SpaRangeTblesNum; Index++) { FREE_POOL_SAFE(ParsedNfit->ppSpaRangeTbles[Index]); } FREE_POOL_SAFE(ParsedNfit->ppSpaRangeTbles); ParsedNfit->SpaRangeTblesNum = 0; for (Index = 0; Index < ParsedNfit->PlatformCapabilitiesTblesNum; Index++) { FREE_POOL_SAFE(ParsedNfit->ppPlatformCapabilitiesTbles[Index]); } FREE_POOL_SAFE(ParsedNfit->ppPlatformCapabilitiesTbles); ParsedNfit->PlatformCapabilitiesTblesNum = 0; } /** Frees the memory associated in the parsed NFit table. @param[in] pParsedNfit pointer to the NFit header. **/ VOID FreeParsedNfit( IN ParsedFitHeader **ppParsedNfit ) { ParsedFitHeader *pParsedNfit = NULL; if (ppParsedNfit == NULL || *ppParsedNfit == NULL) { return; } pParsedNfit = *ppParsedNfit; FREE_POOL_SAFE(pParsedNfit->pFit); FreeNfitSubTables(pParsedNfit); FREE_POOL_SAFE(*ppParsedNfit); } ipmctl-03.00.00.0485/DcpmPkg/common/NvmTables.h000066400000000000000000001067071440615110200205070ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file NvmTables.h * @brief ACPI Data Types for EFI_DCPMM_CONFIG2_PROTOCOL. */ #ifndef _NVM_TABLES_H_ #define _NVM_TABLES_H_ #include #if defined(_MSC_VER) #pragma warning( push ) #pragma warning( disable : 4200 ) #endif /** * @defgroup NFIT_TABLE_TYPES NVDIMM Firmware Interface Table (NFIT) types * @{ **/ #define NVDIMM_SPA_RANGE_TYPE 0 ///< System Physical Address (SPA) range table #define NVDIMM_NVDIMM_REGION_TYPE 1 ///< NVDIMM Region type table #define NVDIMM_INTERLEAVE_TYPE 2 ///< Interleave type table #define NVDIMM_SMBIOS_MGMT_INFO_TYPE 3 ///< SMBIOS MGMT Info type table #define NVDIMM_CONTROL_REGION_TYPE 4 ///< Control Region table #define NVDIMM_BW_DATA_WINDOW_REGION_TYPE 5 ///< BW Data Window Region table #define NVDIMM_FLUSH_HINT_TYPE 6 ///< Flush Hint table #define NVDIMM_PLATFORM_CAPABILITIES_TYPE 7 ///< Platform Capabilities (PCAT) table /** * @} * Fields offsets in structures **/ #define NFIT_TABLE_HEADER_LENGTH_OFFSET 2 #define NFIT_TABLE_HEADER_LENGTH_FIELD_SIZE 2 #define PCAT_TABLE_HEADER_CHECKSUM_OFFSET 9 #define PCAT_TABLE_HEADER_LENGTH_OFFSET 2 #define PCAT_TABLE_HEADER_LENGTH_FIELD_SIZE 2 /** PCAT table types **/ #define PCAT_TYPE_PLATFORM_CAPABILITY_INFO_TABLE 0 #define PCAT_TYPE_INTERLEAVE_CAPABILITY_INFO_TABLE 1 #define PCAT_TYPE_RUNTIME_INTERFACE_TABLE 2 #define PCAT_TYPE_CONFIG_MANAGEMENT_ATTRIBUTES_TABLE 3 #define PCAT_TYPE_SOCKET_SKU_INFO_TABLE 6 #define NUMBER_OF_CHANNEL_WAYS_BITS_NUM 9 #define MIXED_MODE_CAPABILITY_SUPPORTED 1 #define PCAT_HEADER_REVISION_1 1 #define PCAT_HEADER_REVISION_2 2 #define PCAT_HEADER_REVISION_3 3 #define PCAT_HEADER_MINOR_REVISION_1 1 #define PMTT_HEADER_REVISION_1 1 #define PMTT_HEADER_REVISION_2 2 /** PMTT table types**/ #define PMTT_MAX_LEN 4096 #define PMTT_TYPE_SOCKET 0 #define PMTT_TYPE_iMC 1 #define PMTT_TYPE_MODULE 2 #define PMTT_TYPE_VENDOR_SPECIFIC 0XFF #define PMTT_TYPE_RESERVED (BIT2 | BIT3) #define PMTT_COMMON_HDR_LEN 8 #define PMTT_PHYSICAL_ELEMENT_OF_TOPOLOGY BIT1 #define PMTT_DDR_DCPM_FLAG BIT2 #define PMTT_INVALID_SMBIOS_HANDLE 0xFFFFFFFF // PMTT Rev 1.1 GUIDs #define PMTT_TYPE_DIE_GUID \ { 0xA2555053, 0xCDE4, 0x40A5, {0x80, 0x76, 0x00, 0xE3, 0xAB, 0xA6, 0xCA, 0xA7} } #define PMTT_TYPE_CHANNEL_GUID \ { 0x23BF9281, 0xE69c, 0x471F, {0xB2, 0x99, 0xB0, 0x98, 0x2B, 0x2F, 0x55, 0xF9} } #define PMTT_TYPE_SLOT_GUID \ { 0xFDCB2a68, 0xC203, 0x4312, {0xB2, 0x91, 0xB8, 0xE8, 0x62, 0x86, 0xC2, 0xC1} } /** Macros for ACPI Revisions **/ #define ACPI_REVISION_1 1 #define ACPI_REVISION_2 2 #define ACPI_MAJOR_REVISION_1 1 #define ACPI_MAJOR_REVISION_3 3 #define ACPI_MINOR_REVISION_1 1 #define ACPI_MINOR_REVISION_2 2 #define ACPI_MINOR_REVISION_3 3 #define IS_ACPI_REV_MAJ_3_MIN_1(revision) ((revision.Split.Major == ACPI_MAJOR_REVISION_3) && (revision.Split.Minor == ACPI_MINOR_REVISION_1)) #define IS_ACPI_REV_MAJ_3_MIN_2(revision) ((revision.Split.Major == ACPI_MAJOR_REVISION_3) && (revision.Split.Minor == ACPI_MINOR_REVISION_2)) #define IS_ACPI_HEADER_REV_MAJ_3_MIN_1(table) ((table->Header.Revision.Split.Major == ACPI_MAJOR_REVISION_3) && (table->Header.Revision.Split.Minor == ACPI_MINOR_REVISION_1)) #define IS_ACPI_HEADER_REV_MAJ_3_MIN_2(table) ((table->Header.Revision.Split.Major == ACPI_MAJOR_REVISION_3) && (table->Header.Revision.Split.Minor == ACPI_MINOR_REVISION_2)) #define IS_ACPI_REV_MAJ_3_MIN_VALID(revision) (IS_ACPI_REV_MAJ_3_MIN_1(revision) || IS_ACPI_REV_MAJ_3_MIN_2(revision)) #define IS_ACPI_HEADER_REV_MAJ_3_MIN_VALID(table) (IS_ACPI_HEADER_REV_MAJ_3_MIN_1(table) || IS_ACPI_HEADER_REV_MAJ_3_MIN_2(table)) #define IS_ACPI_REV_MAJ_1_MIN_VALID(revision) ((revision.Split.Major == ACPI_MAJOR_REVISION_1) && \ ((revision.Split.Minor == ACPI_MINOR_REVISION_1) || (revision.Split.Minor == ACPI_MINOR_REVISION_2) || \ (revision.Split.Minor == ACPI_MINOR_REVISION_3))) #define IS_ACPI_HEADER_REV_MAJ_1_MIN_VALID(table) ((table->Header.Revision.Split.Major == ACPI_MAJOR_REVISION_1) && \ ((table->Header.Revision.Split.Minor == ACPI_MINOR_REVISION_1) || (table->Header.Revision.Split.Minor == ACPI_MINOR_REVISION_2) || \ (table->Header.Revision.Split.Minor == ACPI_MINOR_REVISION_3))) #define IS_ACPI_HEADER_REV_MAJ_1_MIN_1_OR_2(table) ((table->Header.Revision.Split.Major == ACPI_MAJOR_REVISION_1) && \ ((table->Header.Revision.Split.Minor == ACPI_MINOR_REVISION_1) || (table->Header.Revision.Split.Minor == ACPI_MINOR_REVISION_2))) #define IS_ACPI_REV_MAJ_1_OR_MAJ_3(revision) (IS_ACPI_REV_MAJ_1_MIN_VALID(revision) || IS_ACPI_REV_MAJ_3_MIN_VALID(revision)) #define IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(table) (IS_ACPI_HEADER_REV_MAJ_1_MIN_VALID(table) || IS_ACPI_HEADER_REV_MAJ_3_MIN_VALID(table)) #define IS_ACPI_REV_MAJ_0_MIN_VALID(revision) ((revision.AsUint8 == ACPI_REVISION_1) || (revision.AsUint8 == ACPI_REVISION_2)) #define IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(table) ((table->Header.Revision.AsUint8 == ACPI_REVISION_1) || (table->Header.Revision.AsUint8 == ACPI_REVISION_2)) #define IS_ACPI_REV_MAJ_0_MIN_1(revision) (revision.AsUint8 == ACPI_REVISION_1) #define IS_ACPI_HEADER_REV_MAJ_0_MIN_1(table) (table->Header.Revision.AsUint8 == ACPI_REVISION_1) #define IS_ACPI_REV_MAJ_0_MIN_2(revision) (revision.AsUint8 == ACPI_REVISION_2) #define IS_ACPI_HEADER_REV_MAJ_0_MIN_2(table) (table->Header.Revision.AsUint8 == ACPI_REVISION_2) #define IS_ACPI_REV_INVALID(revision) (((revision.AsUint8 != ACPI_REVISION_1) && (revision.AsUint8 != ACPI_REVISION_2)) && \ ((revision.Split.Major != ACPI_MAJOR_REVISION_1) || (revision.Split.Minor != ACPI_MINOR_REVISION_1)) && \ ((revision.Split.Major != ACPI_MAJOR_REVISION_1) || (revision.Split.Minor != ACPI_MINOR_REVISION_2)) && \ ((revision.Split.Major != ACPI_MAJOR_REVISION_1) || (revision.Split.Minor != ACPI_MINOR_REVISION_3)) && \ ((revision.Split.Major != ACPI_MAJOR_REVISION_3) || (revision.Split.Minor != ACPI_MINOR_REVISION_1)) && \ ((revision.Split.Major != ACPI_MAJOR_REVISION_3) || (revision.Split.Minor != ACPI_MINOR_REVISION_2))) #define IS_ACPI_HEADER_REV_INVALID(table) (((table->Header.Revision.AsUint8 != ACPI_REVISION_1) && (table->Header.Revision.AsUint8 != ACPI_REVISION_2)) && \ ((table->Header.Revision.Split.Major != ACPI_MAJOR_REVISION_1) || (table->Header.Revision.Split.Minor != ACPI_MINOR_REVISION_1)) && \ ((table->Header.Revision.Split.Major != ACPI_MAJOR_REVISION_1) || (table->Header.Revision.Split.Minor != ACPI_MINOR_REVISION_2)) && \ ((table->Header.Revision.Split.Major != ACPI_MAJOR_REVISION_1) || (table->Header.Revision.Split.Minor != ACPI_MINOR_REVISION_3)) && \ ((table->Header.Revision.Split.Major != ACPI_MAJOR_REVISION_3) || (table->Header.Revision.Split.Minor != ACPI_MINOR_REVISION_1)) && \ ((table->Header.Revision.Split.Major != ACPI_MAJOR_REVISION_3) || (table->Header.Revision.Split.Minor != ACPI_MINOR_REVISION_2))) #define IS_NFIT_REVISION_INVALID(revision) (revision.AsUint8 != ACPI_REVISION_1) #define IS_PCAT_REVISION_INVALID(revision) IS_ACPI_REV_INVALID(revision) #define IS_PMTT_REVISION_INVALID(revision) ((revision.AsUint8 != ACPI_REVISION_1) && (revision.AsUint8 != ACPI_REVISION_2)) /** Current Memory Modes definitions as for the PCD/PCAT spec **/ #define VOLATILE_MODE_1LM 0x00 // 00b - 1LM Mode #define VOLATILE_MODE_2LM 0x01 // 01b - Current Memory Mode (2LM) #define VOLATILE_MODE_1LM_OR_2LM 0X01 // 01b - Allowed Memory Mode (1LM or 2LM) #define VOLATILE_MODE_1LM_PLUS_2LM 0x02 // 10b - 1LM+2LM Mode #define PERSISTENT_MODE_DISABLED 0x00 // 00b - Disabled #define PERSISTENT_MODE_APP_DIRECT 0x01 // 01b - AppDirect PM Mode /** NFIT Tables structures **/ #pragma pack(push) #pragma pack(1) /** Intel ACPI Tables Revision **/ typedef union { UINT8 AsUint8; struct { UINT8 Minor:4; UINT8 Major:4; }Split; } ACPI_REVISION; /** NFIT sub-table header */ typedef struct { UINT16 Type; ///< sub-table type UINT16 Length; ///< sub-table length } SubTableHeader; /** ACPI table header. **/ typedef struct { UINT32 Signature; //!< ACPI table signature UINT32 Length; //!< Length in bytes for entire table. It implies the number of Entry fields at the end of the table ACPI_REVISION Revision; //!< table revision UINT8 Checksum; //!< Entire table must sum to zero UINT8 OemId[6]; //!< OEM ID UINT64 OemTableId; //!< the table ID is the manufacturer model ID UINT32 OemRevision; //!< OEM revision of table for supplied OEM table ID UINT32 CreatorId; //!< Vendor ID of utility that created the table UINT32 CreatorRevision; //!< Revision of utility that created the table } TABLE_HEADER; /** NFIT table header */ typedef struct { TABLE_HEADER Header; ///< NFIT Header UINT8 Reserved[4]; ///< Reserved } NFitHeader; /** SPA Range Table */ typedef struct { SubTableHeader Header; ///< Header UINT16 SpaRangeDescriptionTableIndex; ///< SPA Range Description Table index UINT16 Flags; ///< Flags UINT8 Reserved[4]; ///< Reserved UINT32 ProximityDomain; ///< Proximity Domain GUID AddressRangeTypeGuid; ///< Address Range Type GUID UINT64 SystemPhysicalAddressRangeBase; ///< System Physical Address Range Base UINT64 SystemPhysicalAddressRangeLength; ///< Systems Physical Address Range Length UINT64 AddressRangeMemoryMappingAttribute; ///< Address Range Memory Mapping Attributes } SpaRangeTbl; /** NFIT Device Handle */ typedef union { struct { UINT32 DimmNumber:4; ///< PMem module Number UINT32 MemChannel:4; ///< Memory Channel UINT32 MemControllerId:4; ///< Memory Controller ID UINT32 SocketId:4; ///< Socket ID UINT32 NodeControllerId:12; ///< Node Controller ID UINT32 Reserved:4; ///< Reserved } NfitDeviceHandle; UINT32 AsUint32; ///< Combined value } NfitDeviceHandle; /** PMem module Region table */ typedef struct { SubTableHeader Header; ///< Header NfitDeviceHandle DeviceHandle; ///< PMem module Handle UINT16 NvDimmPhysicalId; ///< Physical ID UINT16 NvDimmRegionalId; ///< Region ID UINT16 SpaRangeDescriptionTableIndex; ///< SPA Range Description table index UINT16 NvdimmControlRegionDescriptorTableIndex; ///< Control Region Descriptor table index UINT64 NvDimmRegionSize; ///< Region Size UINT64 RegionOffset; ///< Region Offset UINT64 NvDimmPhysicalAddressRegionBase; ///< Physical Address Range Base UINT16 InterleaveStructureIndex; ///< Interleave Structure Index UINT16 InterleaveWays; ///< Interleave Ways UINT16 NvDimmStateFlags; ///< State Flags UINT8 Reserved[2]; ///< Reserved } NvDimmRegionMappingStructure; /** Interleave table */ typedef struct { SubTableHeader Header; ///< Header UINT16 InterleaveStructureIndex; ///< Interleave structure index UINT8 Reserved[2]; ///< Reserved UINT32 NumberOfLinesDescribed; ///< Number of lines described UINT32 LineSize; ///< Line size UINT32 LinesOffsets[0]; ///< Line offsets } InterleaveStruct; /** SMBIOS table*/ typedef struct { SubTableHeader Header; ///< Header UINT8 Reserved[4]; ///< Reserved UINT8 Data[0]; ///< Data } SmbiosTbl; /** Control Region Table */ typedef struct { SubTableHeader Header; ///< Header UINT16 ControlRegionDescriptorTableIndex; ///< Control Region Descriptor Table index UINT16 VendorId; ///< Vendor ID UINT16 DeviceId; ///< Device ID UINT16 Rid; ///< Revision ID UINT16 SubsystemVendorId; ///< Subsystem Vendor ID UINT16 SubsystemDeviceId; ///< Subsystem Device ID UINT16 SubsystemRid; ///< Subsystem Revision ID UINT8 ValidFields; ///< Valid Fields UINT8 ManufacturingLocation; ///< Manufacturing Location UINT16 ManufacturingDate; ///< Manufacturing Date UINT8 Reserved[2]; ///< Reserved UINT32 SerialNumber; ///< Serial Number UINT16 RegionFormatInterfaceCode; ///< Region format interface code UINT16 NumberOfBlockControlWindows; ///< Number of block control windows UINT64 SizeOfBlockControlWindow; ///< Size of block control window UINT64 CommandRegisterOffsetInBlockControlWindow; ///< Command register offset in block control window UINT64 SizeOfCommandRegisterInBlockControlWindows; ///< Size of command register UINT64 StatusRegisterOffsetInBlockControlWindow; ///< Status register offset in block control window UINT64 SizeOfStatusRegisterInBlockControlWindows; ///< Size of status register UINT16 ControlRegionFlag; ///< Control region flags UINT8 Reserved1[6]; ///< Reserved } ControlRegionTbl; /** BW Region table */ typedef struct { SubTableHeader Header; ///< Header UINT16 ControlRegionStructureIndex; ///< Control region structure index UINT16 NumberOfBlockDataWindows; ///< Number of block data windows UINT64 BlockDataWindowStartLogicalOffset; ///< Block data window starting logical offset UINT64 SizeOfBlockDataWindow; ///< Size of block data window UINT64 AccessibleBlockCapacity; ///< Accessible block capacity UINT64 AccessibleBlockCapacityStartAddress; ///< Accessible block capacity start address } BWRegionTbl; /** Flush Hint table */ typedef struct { SubTableHeader Header; ///< Header NfitDeviceHandle DeviceHandle; ///< Device handle UINT16 NumberOfFlushHintAddresses; ///< Number of flush hint addresses UINT8 Reserved[6]; ///< Reserved UINT64 FlushHintAddress[0]; ///< Flush hint addresses } FlushHintTbl; #define CAPABILITY_CACHE_FLUSH BIT0 #define CAPABILITY_MEMORY_FLUSH BIT1 #define CAPABILITY_MEMORY_MIRROR BIT2 /** Platform Capabilities table */ typedef struct { SubTableHeader Header; ///< Header UINT8 HighestValidCapability; ///< Highest valid capability UINT8 Reserved[3]; ///< Reserved UINT32 Capabilities; ///< Capabilities UINT32 Reserved_1; ///< Reserved } PlatformCapabilitiesTbl; /** NFIT ACPI data */ typedef struct { NFitHeader *pFit; ///< NFIT Header UINT32 SpaRangeTblesNum; ///< Count of SPA Range tables SpaRangeTbl **ppSpaRangeTbles; ///< SPA Range tables UINT32 NvDimmRegionMappingStructuresNum; ///< Count of Region tables NvDimmRegionMappingStructure **ppNvDimmRegionMappingStructures; ///< Region tables UINT32 InterleaveTblesNum; ///< Count of interleave tables InterleaveStruct **ppInterleaveTbles; ///< Interleave tables UINT32 SmbiosTblesNum; ///< Count of SMBIOS tables SmbiosTbl **ppSmbiosTbles; ///< SMBIOS tables UINT32 ControlRegionTblesNum; ///< Count of Control Region tables ControlRegionTbl **ppControlRegionTbles; ///< Control region tables UINT32 BWRegionTblesNum; ///< Count of BW Region tables BWRegionTbl **ppBWRegionTbles; ///< BW Region tables UINT32 FlushHintTblesNum; ///< Count of Flush Hint Tables FlushHintTbl **ppFlushHintTbles; ///< Flush Hint tables UINT32 PlatformCapabilitiesTblesNum; ///< Count of PCAT tables PlatformCapabilitiesTbl **ppPlatformCapabilitiesTbles; ///< PCAT tables } ParsedFitHeader; typedef struct { UINT16 Type; //!< Type of PCAT table UINT16 Length; //!< Length of the table including the header and body } PCAT_TABLE_HEADER; typedef struct { /** HEADER **/ TABLE_HEADER Header; //!< Signature for this table: 'PCAT' UINT8 Reserved[4]; /** BODY **/ /** A list of PCAT table structures **/ VOID *pPcatTables[0]; } PLATFORM_CONFIG_ATTRIBUTES_TABLE; typedef union { UINT16 AsUint16; struct { UINT16 PerDie : 4; UINT16 PerDcpmm : 4; UINT16 Reserved : 8; } MaxInterleaveSetsSplit; } MAX_PMINTERLEAVE_SETS; typedef union { UINT8 MemoryModes; struct { UINT8 OneLm : 1; UINT8 Memory : 1; UINT8 AppDirect : 1; UINT8 Reserved1 : 1; UINT8 MixedMode : 1; UINT8 Reserved2 : 3; } MemoryModesFlags; } SUPPORTED_MEMORY_MODE3; typedef union { UINT8 MemoryModes; struct { UINT8 OneLm :1; UINT8 Memory :1; UINT8 AppDirect :1; UINT8 Reserved1 :2; UINT8 SubNUMACluster :1; UINT8 Reserved :2; } MemoryModesFlags; } SUPPORTED_MEMORY_MODE; typedef struct { UINT8 CurrentVolatileMode : 2; UINT8 PersistentMode : 2; UINT8 AllowedVolatileMode : 2; UINT8 Reserved : 2; } _MEMORY_MODE_SPLIT3; typedef union { UINT8 MemoryMode; _MEMORY_MODE_SPLIT3 MemoryModeSplit; } CURRENT_MEMORY_MODE3; typedef struct { UINT8 CurrentVolatileMode : 2; UINT8 PersistentMode : 2; UINT8 AllowedVolatileMode : 2; UINT8 Reserved : 1; UINT8 SubNumaCluster : 1; } _MEMORY_MODE_SPLIT; typedef union { UINT8 MemoryMode; _MEMORY_MODE_SPLIT MemoryModeSplit; } CURRENT_MEMORY_MODE; typedef struct { UINT8 InTile : 1; UINT8 CrossTile : 1; UINT8 Reserved : 2; } MEM_MODE_CACHE_CAPABILITIES; typedef union { UINT8 AsUint8; struct { MEM_MODE_CACHE_CAPABILITIES MemoryMode; UINT8 Reserved : 4; } CacheCapabilitiesSplit; } CACHE_CAPABILITIES; typedef struct { /** HEADER **/ PCAT_TABLE_HEADER Header; //!< Type: 0 /** BODY **/ /** Bit0 If set BIOS supports changing configuration through management software. If clear BIOS does not allow configure change through management software Bits[7:1]: Reserved **/ UINT8 MgmtSwConfigInputSupport; /** Bit0: Set if 1LM Mode supported Bit1: Set if 2LM Mode supported Bit2: Set if AppDirect Mode supported Bits[7:3]: Reserved **/ SUPPORTED_MEMORY_MODE3 MemoryModeCapabilities; /** Memory Mode selected in the BIOS setup Bits[1:0] - Current Volatile Memory Mode 00b - 1LM Mode 01b - 2LM Mode 10b - Reserved 11b - Reserved Bits[3:2] - Allowed Persistent Memory Mode 00b - None 01b - App Direct Mode 10b - Reserved 11b - Reserved Bits[5:4] - Allowed Volatile Memory Mode 00b - 1LM Mode Only 01b - 1LM or 2LM Mode 10b - Reserved 11b - Reserved Bits[7:6] - Reserved Note: no direct control is given to the management software to switch the mode **/ CURRENT_MEMORY_MODE3 CurrentMemoryMode; /** Bits[3-0]: per CPU Die Bits[7-4]: per module Bits[15-8]: Reserved 0 means there is no limit defined. **/ MAX_PMINTERLEAVE_SETS MaxPMInterleaveSets; /** Added as part of 3.01 F-S specification Capacity in GiB per DDR DIMM for use as near memory cache if 2LM is enabled. The remaining DDR capacity will be used as 1LM. **/ UINT32 DDRCacheSize; /** Added as part of 3.01 F-S specification Cache Capabilities supported Bits[3-0] - MemoryMode Cache Capabilities Bit0: Set if In-tile supported Bit1: Set if Cross-tile supported Bits[3:2]: Reserved Bits[7:4] - Reserved **/ CACHE_CAPABILITIES CacheCapabilities; UINT8 Reserved2[2]; } PLATFORM_CAPABILITY_INFO3; typedef struct { /** HEADER **/ PCAT_TABLE_HEADER Header; //!< Type: 0 /** BODY **/ /** Bit0 If set BIOS supports changing configuration through management software. If clear BIOS does not allow configure change through management software Bit1 If set BIOS supports runtime interface to validate management configuration change request. Refer to BIOS runtime interface data structure. Note: this bit is valid only if Bit0 is set. **/ UINT8 MgmtSwConfigInputSupport; /** Bit0: Set if 1LM Mode supported Bit1: Set if 2LM Mode supported Bit2: Set if PM-Direct Mode supported Bit5: Set if SubNUMA Cluster supported **/ SUPPORTED_MEMORY_MODE MemoryModeCapabilities; /** Memory Mode selected in the BIOS setup 0 - 1LM mode 1 - 2LM mode 2 - Auto (2LM if DDR+PMM with volatile mode present, 1LM otherwise) Note: no direct control is given to the management software to switch the mode **/ CURRENT_MEMORY_MODE CurrentMemoryMode; /** Bit0: If set Persistent Memory region mirroring is supported Bit1: DimmSpareSupported Bit2: AppDirectMigrationSupported **/ UINT8 PersistentMemoryRasCapability; UINT8 Reserved[8]; } PLATFORM_CAPABILITY_INFO; typedef union { UINT16 AsUint16; struct { UINT16 PerDie : 4; UINT16 PerDcpmm : 4; UINT16 Reserved : 8; } MaxInterleaveSetsSplit; } MAX_INTERLEAVE_SETS_PER_MEMTYPE; typedef union { UINT16 AsUint16; struct { UINT16 ChannelInterleaveSize : 8; UINT16 iMCInterleaveSize : 8; } InterleaveSizeSplit; } INTERLEAVE_SIZE; typedef union { UINT32 AsUint32; struct { UINT32 InterleaveMap : 16; UINT32 Recommended : 1; UINT32 Reserved : 15; } InterleaveFormatSplit; } INTERLEAVE_FORMAT3; typedef union { UINT32 AsUint32; struct { UINT32 ChannelInterleaveSize:8; UINT32 iMCInterleaveSize :8; UINT32 NumberOfChannelWays :NUMBER_OF_CHANNEL_WAYS_BITS_NUM; UINT32 Reserved :6; UINT32 Recommended :1; } InterleaveFormatSplit; } INTERLEAVE_FORMAT; typedef struct { /** HEADER **/ PCAT_TABLE_HEADER Header; //!< Type: 1 /** BODY **/ /** Value defines memory mode 3 - App Direct PM **/ UINT8 MemoryMode; UINT8 Reserved[3]; /** Interleave alignment size in 2^n bytes. n=26 for 64MB n=27 for 128MB **/ UINT16 InterleaveAlignmentSize; /** Byte0 - Supported Channel interleave size Bit0 - 4KB Bit1 - 256B Bits[7-2] - Reserved Byte1 - Supported iMC interleave size Bit0 - 4KB Bit1 - 256B Bits[7-2] - Reserved **/ INTERLEAVE_SIZE InterleaveSize; /** Bits[3-0]: per CPU Die Bits[7-4]: per module Bits[15-8]: Reserved 0 means there is no limit defined. **/ MAX_INTERLEAVE_SETS_PER_MEMTYPE MaxInterleaveSetsPerMemType; /** Number of interleave formats supported by BIOS for the previously mentioned memory mode. The variable body of this structure contains m number of interleave formats. **/ UINT16 NumOfFormatsSupported; /** This field will have a list of 4-byte values that provide information about BIOS supported interleave formats and the recommended interleave informations. BIOS populates the list based on CPU platform and BIOS settings. (x1 way) IMC0 IMC1 CH0 | 0b000001 | 0b000010 | CH1 | 0b000100 | 0b001000 | CH2 | 0b010000 | 0b100000 | Byte[0-1] - Interleave Bitmap based on physical PMem module population. For example: Purley Platform 0b001111 x4 0b111100 x4 0b110011 x4 0b010101 x3 0b101010 x3 ... ... Byte2 - Flags Bit0 - Recommended If clear, the interleave format is supported but not recommended. If set, the interleave format is recommended. Bits[1-7] - Reserved Byte3 - Reserved **/ INTERLEAVE_FORMAT3 InterleaveFormatList[0]; } MEMORY_INTERLEAVE_CAPABILITY_INFO3; typedef struct { /** HEADER **/ PCAT_TABLE_HEADER Header; //!< Type: 1 /** BODY **/ /** Value defines memory mode 0 - 1LM 1 - 2LM 3 - App Direct PM **/ UINT8 MemoryMode; UINT8 Reserved[3]; /** Interleave alignment size in 2^n bytes. n=26 for 64MB n=27 for 128MB **/ UINT16 InterleaveAlignmentSize; /** Number of interleave formats supported by BIOS for the previously mentioned memory mode. The variable body of this structure contains m number of interleave formats. **/ UINT16 NumOfFormatsSupported; /** This field will have a list of 4-byte values that provide information about BIOS supported interleave formats and the recommended interleave informations. Byte0 - Channel interleave size Bit0 - 64B Bit1 - 128B Bit2 - 256B Bit3 - Reserved Bit4 - Reserved Bit5 - Reserved Bit6 - 4KB Bit7 - 1GB Byte1 - iMC interleave size Bit0 - 64B Bit1 - 128B Bit2 - 256B Bit3 - Reserved Bit4 - Reserved Bit5 - Reserved Bit6 - 4KB Bit7 - 1GB Byte2-3 - Number of channel ways Bit0 - 1way Bit1 - 2way Bit2 - 3way Bit3 - 4way Bit4 - 6way Bit5 - 8way Bit6 - 12way Bit7 - 16way Bit8 - 24way Bit9-14 - Reserved Byte2-3 - Recommended Interleave format Bit15 - If clear, the interleave format is supported but not recommended. If set, the interleave format is recommended. **/ INTERLEAVE_FORMAT InterleaveFormatList[0]; } MEMORY_INTERLEAVE_CAPABILITY_INFO; typedef struct { /** HEADER **/ PCAT_TABLE_HEADER Header; //!< Type: 2 /** BODY **/ /** Verify Trigger GAS Structure **/ /** Address space type of command register. 1 - System I/O **/ UINT8 AddressSpaceId; UINT8 BitWidth; //!< The size in bits of the command register UINT8 BitOffset; //!< The bit offset command register at the given address /** Command register access size 0 - Undefined 1 - Byte Access 2 - Word Access 3 - Dword Access 4 - Qword Access **/ UINT8 AccessSize; UINT64 Address; //!< Register in the given address space /** Verify Trigger Operation **/ /** Type of register operation to submit the command 0 - Read register 1 - Write register **/ UINT8 TriggerOperationType; UINT8 Reserved2[7]; UINT64 TriggerValue; //!< If operation type is write, this field provides the data to be written /** Mask value to be used to preserve the bits on the write. If the bits are not 1, read the value from the address space, mask the value part and then do the write **/ UINT64 TriggerMask; /** Verify Status Operation **/ /** ACPI GAS structure with Address Space ID. 0 - System memory **/ UINT8 GasStructure[12]; /** Type of register operation to submit the command 3 - Read Memory **/ UINT8 StatusOperationType; UINT8 Reserved3[3]; /** Read the value from given address and mask using this value. Result status: 0 - None 1 - Busy 2 - Done Results are updated in the PMem modules' config output structures **/ UINT64 StatusMask; } RECONFIGURATION_INPUT_VALIDATION_INTERFACE_TABLE; typedef struct { /** HEADER **/ PCAT_TABLE_HEADER Header; //!< Type: 3 /** BODY **/ UINT8 Reserved[2]; UINT16 VendorId; //!< Vendor ID of generator of the GUID who maintains the format for the GUID data EFI_GUID Guid; VOID *pGuidData[0]; //!< GUID Data Size must be 8-byte aligned } CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE; typedef struct { /** HEADER **/ PCAT_TABLE_HEADER Header; //!< Type: 6 /** BODY **/ UINT16 SocketId; UINT16 DieId; UINT64 MappedMemorySizeLimit; //!< Total amount of physical memory in bytes allowed to be mapped into SPA based on the SKU of the CPU specified by Socket ID UINT64 TotalMemorySizeMappedToSpa; //!< Total amount of physical memory in bytes currently mapped into the SPA for the CPU specified by Socket ID UINT64 CachingMemorySize; //!< Total amount of physical memory in bytes used for caching when the system is in 2LM mode } DIE_SKU_INFO_TABLE; typedef struct { /** HEADER **/ PCAT_TABLE_HEADER Header; //!< Type: 6 /** BODY **/ UINT16 SocketId; //!< Zero indexed NUMA node identifier UINT8 Reserved[2]; UINT64 MappedMemorySizeLimit; //!< Total amount of physical memory in bytes allowed to be mapped into SPA based on the SKU of the CPU specified by Socket ID UINT64 TotalMemorySizeMappedToSpa; //!< Total amount of physical memory in bytes currently mapped into the SPA for the CPU specified by Socket ID UINT64 CachingMemorySize; //!< Total amount of physical memory in bytes used for caching when the system is in 2LM mode } SOCKET_SKU_INFO_TABLE; /** PMTT Rev 2 table**/ typedef struct { TABLE_HEADER Header; UINT32 NoOfMemoryDevices; VOID *pPmttDevices[0]; } PMTT_TABLE2; /* Header common to socket, iMC and Module*/ typedef struct { /* * Type of aggregated device * 0 - Socket * 1 - Memory controller * 2 - Module * 3 - 0xFF */ UINT8 Type; UINT8 Reserved1; /* * Length in bytes for entire table. */ UINT16 Length; UINT16 Flags; UINT16 Reserved2; UINT32 NoOfMemoryDevices; } PMTT_COMMON_HEADER2; /* * Memory device structure * Type 0 - socket */ typedef struct { PMTT_COMMON_HEADER2 Header; UINT16 SocketId; UINT16 Reserved3; // Die structure follows this } PMTT_SOCKET2; /* * Memory device structure * Type 1 - iMC * HMAT/SLIT tables should be used for latency and bandwidth information, removed from PMTT Rev. 2 */ typedef struct { PMTT_COMMON_HEADER2 Header; UINT16 MemControllerID; UINT16 Reserved3; // Module structure follows this } PMTT_iMC2; /* * Vendor device structure * Type 0XFF - Die/Channel/Slot */ typedef struct { PMTT_COMMON_HEADER2 Header; GUID TypeUUID; UINT16 DeviceID; UINT16 Reserved3; // Vendor specific data follows this } PMTT_VENDOR_SPECIFIC2; /* * Memory device structure * Type 2 - Module */ typedef struct { PMTT_COMMON_HEADER2 Header; UINT32 SmbiosHandle; } PMTT_MODULE2; typedef struct { PMTT_COMMON_HEADER2 Header; UINT8 MemoryType; UINT16 SmbiosHandle; UINT16 SocketId; UINT16 DieId; UINT16 CpuId; UINT16 MemControllerId; UINT16 ChannelId; UINT16 SlotId; } PMTT_MODULE_INFO; /** PMTT Rev 0x11 ACPI data Enabled in ParsedPmttHeader means there is at least one PMem module or DDR under iMC, channel or slot type devices. **/ typedef struct { PMTT_TABLE2 *pPmtt; ///< PMTT Header UINT32 iMCsNumPerDie; ///< Count of iMCs per Die UINT32 ChannelsNumPeriMC; ///< Count of Channels per iMC UINT32 SocketsNum; ///< Count of Socket Devices PMTT_SOCKET2 **ppSockets; ///< Socket Type Data Tables UINT32 DiesNum; ///< Count of Die Devices PMTT_VENDOR_SPECIFIC2 **ppDies; ///< Die Type Data Tables UINT32 iMCsNum; ///< Count of enabled Memory Controller Devices PMTT_iMC2 **ppiMCs; ///< iMC Type Data Tables UINT32 ChannelsNum; ///< Count of enabled Channel Devices PMTT_VENDOR_SPECIFIC2 **ppChannels; ///< Channel Type Data Tables UINT32 SlotsNum; ///< Count of enabled Slot Devices PMTT_VENDOR_SPECIFIC2 **ppSlots; ///< Slot Type Data Tables UINT32 DDRModulesNum; ///< Count of DDR Devices PMTT_MODULE_INFO **ppDDRModules; ///< DDR Module Type Data Tables UINT32 DCPMModulesNum; ///< Count of PMem module Devices PMTT_MODULE_INFO **ppDCPMModules; ///< PMem module Type Data Tables } ParsedPmttHeader; /** PMTT table **/ typedef struct { TABLE_HEADER Header; UINT8 Reserved[4]; VOID *PMTTAggregatedDevices[0]; } PMTT_TABLE; /* Header common to socket, iMC and Module*/ typedef struct { /* * Type of aggregated device * 0 - Socket * 1 - Memory controller * 2 - Module * 3 - 0xFF */ UINT8 Type; UINT8 Reserved1; /* * Length in bytes for entire table. */ UINT16 Length; UINT16 Flags; UINT16 Reserved2; } PMTT_COMMON_HEADER; /* * Memory aggregator device structure * Type 0 - socket */ typedef struct { UINT16 SocketId; UINT16 Reserved3; // Memory controller comes here } PMTT_SOCKET; /* * Memory aggregator device structure * Type 1 - iMC */ typedef struct { UINT32 ReadLatency; UINT32 WriteLatency; UINT32 ReadBW; UINT32 WriteBW; UINT16 OptimalAccessUnit; UINT16 OptimalAccessAlignment; UINT16 Reserved3; UINT16 NoOfProximityDomains; UINT32 ProximityDomainArray; //!< Supposed to be an array but BIOS is filling in 0s for now // Module structure follows this } PMTT_iMC; /* * Memory aggregator device structure * Type 2 - Module */ typedef struct { UINT16 PhysicalComponentId; UINT16 Reserved3; UINT32 SizeOfDimm; UINT32 SmbiosHandle; } PMTT_MODULE; #pragma pack(pop) /** PCAT version specific tables **/ typedef union { struct { PLATFORM_CAPABILITY_INFO **ppPlatformCapabilityInfo; MEMORY_INTERLEAVE_CAPABILITY_INFO **ppMemoryInterleaveCapabilityInfo; SOCKET_SKU_INFO_TABLE **ppSocketSkuInfoTable; } Pcat2Tables; struct { PLATFORM_CAPABILITY_INFO3 **ppPlatformCapabilityInfo; MEMORY_INTERLEAVE_CAPABILITY_INFO3 **ppMemoryInterleaveCapabilityInfo; DIE_SKU_INFO_TABLE **ppDieSkuInfoTable; } Pcat3Tables; } PCAT_VERSION; typedef struct { PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttr; PCAT_VERSION pPcatVersion; UINT32 PlatformCapabilityInfoNum; UINT32 MemoryInterleaveCapabilityInfoNum; RECONFIGURATION_INPUT_VALIDATION_INTERFACE_TABLE **ppRuntimeInterfaceValConfInput; UINT32 RuntimeInterfaceValConfInputNum; CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE **ppConfigManagementAttributesInfo; UINT32 ConfigManagementAttributesInfoNum; UINT32 SocketSkuInfoNum; } ParsedPcatHeader; /** Frees the memory associated in the parsed PCAT table. @param[in, out] pParsedPcat pointer to the PCAT header. **/ VOID FreeParsedPcat( IN OUT ParsedPcatHeader **ppParsedPcat ); /** Frees the memory associated in the parsed PMTT table. @param[in, out] pParsedPmtt pointer to the PMTT header. **/ VOID FreeParsedPmtt( IN OUT ParsedPmttHeader **ppParsedPmtt ); /** Frees the memory associated in the parsed NFit table. @param[in] pParsedNfit pointer to the NFit header. **/ VOID FreeParsedNfit( IN ParsedFitHeader **ppParsedNfit ); #if defined(_MSC_VER) #pragma warning( pop ) #endif #endif /** _NVM_TABLES_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/NvmTypes.h000066400000000000000000001771141440615110200204010ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file NvmTypes.h * @brief Types for EFI_DCPMM_CONFIG2_PROTOCOL to configure and manage PMem modules. */ #ifndef _NVM_TYPES_H_ #define _NVM_TYPES_H_ #include "NvmLimits.h" #include "NvmWorkarounds.h" #ifdef OS_BUILD #include #include "os_types.h" #include #endif // !OS_BUILD #include #define MAX_UINT8_VALUE 0xFF #define MAX_UINT16_VALUE 0xFFFF #define MAX_UINT32_VALUE 0xFFFFFFFF #define MAX_UINT64_VALUE 0xFFFFFFFFFFFFFFFFULL /** Needed for HII form. There is an error that the HII form is always displaying the value as signed, so skip the highest bit, so that it will always stay at positive numbers. **/ #define MAX_UINT31_VALUE 0x7FFFFFFF #define MAX_UINT63_VALUE 0x7FFFFFFFFFFFFFFF /** Those defines describe the amount of bits and digits for the internal loops in UINT128 to string functions. **/ #define UINT32_DEC_MB_STRING_SIZE 12 //!< Length of string with decimal UINT32 value with "MB" suffix #define UINT8_HEX_STRING_SIZE 6 #define UINT16_HEX_STRING_SIZE 14 #define UINT32_HEX_STRING_SIZE 26 #define UINT128_DIGITS 39 #define HII_CAPACITY_STRING_SIZE 19 #define HII_SENSOR_VALUE_STRING_SIZE 30 // only 22 are needed, but might as well be generous #define HII_HEALTH_REASON_STRING_SIZE 110 #define HII_ISET_ID_STRING_SIZE 64 #define HII_DIMM_ID_STRING_SIZE 255 #define HII_APPDIRECT_PREFERENCE_OPTION_SIZE 30 /** UINT128_DIGITS + 1; it has to be a number because of VFR compiler requirements **/ #define UINT128_STRING_SIZE 40 #define UINT64_BITS 64 #define REGION_TYPE_STR_SIZE 50 #define REGION_HEALTH_STR_SIZE 9 #define REGION_GOAL_SETTINGS_STR_SIZE 32 #define REGION_GOAL_INDEX_STR_SIZE 10 #define NSLABEL_NAME_LEN 63 //!< Namespace name length #define NLABEL_NAME_LEN_WITH_TERMINATOR 64 //!< Namespace name length + null terminator #define NSGUID_LEN 16 //!< Length of GUID in bytes #define GUID_STR_LEN 36 //!< Length of GUID string representation (in characters) #define LIST_ENTRY_SIZE 16 //!< Size of list entry field (two pointers) #define SENSOR_HEALTH_STR_SIZE 19 #define LABEL_VERSION_STR_SIZE 4 #define HEALTH_STR_SIZE 19 #define FEATURE_NOT_SUPPORTED 0 #define FEATURE_SUPPORTED 1 typedef struct { UINT64 Uint64; UINT64 Uint64_1; } UINT128; #define FW_BCD_VERSION_LEN 5 #define FW_VERSION_LEN 19 #ifdef OS_BUILD #define FW_API_VERSION_LEN 20 #else #define FW_API_VERSION_LEN 6 #endif #define MANUFACTURER_LEN 19 #define SERIAL_NUMBER_LEN 19 #define PART_NUMBER_LEN 21 #define PART_NUMBER_STR_LEN 22 #define DEVICE_LOCATOR_LEN 128 #define BANKLABEL_LEN 32 // @todo confirm label length #define SHUTDOWN_STATUS_LEN 255 // @todo confirm label length #define SECURITY_CAPABILITIES_LEN 18 // @todo confirm label length #define IFC_STRING_LEN 255 #define MEMORY_MODES_LEN 40 // @todo confirm label length #define MEMORY_TYPE 30 #define BOOT_STATUS_LEN 100 #define FW_UPDATE_STATUS_LEN 64 #define QUIESCE_REQUIRED_LEN 30 #define STAGED_FW_ACTIVATABLE_LEN 50 #define FW_BUILD_LEN 16 #define FW_BUILD_STR_LEN 17 #define FW_COMMIT_ID_LEN 40 #define FW_COMMIT_ID_STR_LEN 41 #define DATE_STR_LEN 29 #define SW_TRIG_ENABLED_DETAILS_LEN 120 #define CONTROLLER_RID_LEN 12 #define SECURITY_STATE_STR_LEN 32 #define SVN_DOWNGRADE_STR_LEN 30 #define S3_RESUME_STR_LEN 30 #define SECURE_ERASE_POLICY_STR_LEN 30 #define FW_ACTIVATE_STR_LEN 30 #define FIPS_MODE_STATUS_MAX_STR_LEN 65 // STR_DCPMM_FIPS_MODE_STATUS_NON_FIPS_MODE_UNTIL_NEXT_BOOT /** PMem module UID length, including null terminator **/ #define MAX_DIMM_UID_LENGTH 22 //!< PMem module UID hexadecimal-format representation length, including manufacturing fields #define MIN_DIMM_UID_LENGTH 14 //!< PMem module UID hexadecimal-format representation length, excluding manufacturing fields #define NVM_EVENT_MSG_LEN 1024 // Length of event message string #ifdef OS_BUILD #define NVM_SYSLOG_SOURCE "NVM_MGMT" #define NVM_SYSLOG_SRC_W L"NVM_MGMT" #define NVM_DEBUG_LOGGER_SOURCE "NVM_DBG_LOGGER" #define NVM_DIMM_NAME "Intel Persistent Memory Module" #define MAX_SOURCE_STR_LENGTH 32 typedef char NVM_EVENT_MSG[NVM_EVENT_MSG_LEN]; // Event message string typedef wchar_t NVM_EVENT_MSG_W[NVM_EVENT_MSG_LEN]; // Event message string #endif // OS_BUILD /** Memory modes of Memory Interleave Capability Information table **/ #define PCAT_MEMORY_MODE_1LM 0 #define PCAT_MEMORY_MODE_2LM 1 #define PCAT_MEMORY_MODE_PM_DIRECT 3 #define PCAT_MEMORY_MODE_PM_CACHED 4 #define MODE_DISABLED 0 #define MODE_ENABLED 1 #define CHANNEL_INTERLEAVE_SIZE_VARIABLE_NAME L"CHANNEL_INTERLEAVE_SIZE" /** Channel interleave size **/ #define CHANNEL_INTERLEAVE_SIZE_INVALID MAX_UINT8_VALUE #define CHANNEL_INTERLEAVE_SIZE_64B BIT0 #define CHANNEL_INTERLEAVE_SIZE_128B BIT1 #define CHANNEL_INTERLEAVE_SIZE_256B BIT2 #define CHANNEL_INTERLEAVE_SIZE_4KB BIT6 #define CHANNEL_INTERLEAVE_SIZE_1GB BIT7 #define IMC_INTERLEAVE_SIZE_VARIABLE_NAME L"IMC_INTERLEAVE_SIZE" #define APPDIRECT_GRANULARITY_1GIB 1 #define APPDIRECT_GRANULARITY_32GIB 0 #define APPDIRECT_GRANULARITY_MAX APPDIRECT_GRANULARITY_1GIB #define APPDIRECT_GRANULARITY_DEFAULT APPDIRECT_GRANULARITY_1GIB #define PBR_CONTEXT_VAR L"PBR_CONTEXT" #define PBR_TAG_ID_VAR L"PBR_TAG" /** IMC interleave size **/ #define IMC_INTERLEAVE_SIZE_INVALID MAX_UINT8_VALUE #define IMC_INTERLEAVE_SIZE_64B BIT0 #define IMC_INTERLEAVE_SIZE_128B BIT1 #define IMC_INTERLEAVE_SIZE_256B BIT2 #define IMC_INTERLEAVE_SIZE_4KB BIT6 #define IMC_INTERLEAVE_SIZE_1GB BIT7 /** Number of channel ways **/ #define INTERLEAVE_SET_1_WAY BIT0 #define INTERLEAVE_SET_2_WAY BIT1 #define INTERLEAVE_SET_3_WAY BIT2 #define INTERLEAVE_SET_4_WAY BIT3 #define INTERLEAVE_SET_6_WAY BIT4 #define INTERLEAVE_SET_8_WAY BIT5 #define INTERLEAVE_SET_12_WAY BIT6 #define INTERLEAVE_SET_16_WAY BIT7 #define INTERLEAVE_SET_24_WAY BIT8 /** Persistent memory types VFR compiler supports only basic #defines, that is why bare values are used **/ #define PM_TYPE_AD 0x1 // BIT0 #define PM_TYPE_AD_NI 0x2 // BIT1 #define PM_TYPE_RESERVED 0x4 // BIT2 #define PM_TYPE_ALL 0x7 // (PM_TYPE_AD | PM_TYPE_AD_NI | PM_TYPE_RESERVED) #define RECOMMENDED_SETTINGS BIT0 /** Namespace Label Version - int representation of version **/ #define NS_LABEL_VERSION_LATEST 0 #define NS_LABEL_VERSION_1_1 1 #define NS_LABEL_VERSION_1_2 2 /** * @defgroup GOAL_CONFIG_STATUS Goal Configuration Status * @{ */ #define GOAL_CONFIG_STATUS_NO_GOAL_OR_SUCCESS 0 ///< No goal or goal applied successfully #define GOAL_CONFIG_STATUS_UNKNOWN 1 ///< Unknown status #define GOAL_CONFIG_STATUS_NEW 2 ///< Goal is new, not yet applied. Reboot required to apply. #define GOAL_CONFIG_STATUS_BAD_REQUEST 3 ///< Goal request is invalid #define GOAL_CONFIG_STATUS_NOT_ENOUGH_RESOURCES 4 ///< Unable to apply goal because not enough resources #define GOAL_CONFIG_STATUS_FIRMWARE_ERROR 5 ///< Unable to apply goal because of a FW error #define GOAL_CONFIG_STATUS_FAILED_UNKNOWN 6 ///< Unable to apply goal. Internal error /** Goal Config data type has two elements AppDirect1 and AppDirect2 regions. **/ #define APPDIRECT_1_INDEX 0 #define APPDIRECT_2_INDEX 1 /** * @} * Firmware types */ #define FW_TYPE_PRODUCTION 29 #define FW_TYPE_DFX 30 #define FW_TYPE_DEBUG 31 /** in structures visible to VFR form. One solution is to use 64-bit integer as a pointer and In UDK2010.SR1.UP1 HII parser does not understand '*' symbol, so pointers cannot be used cast whenever necessary. **/ #define HII_POINTER UINT64 #define SUPPORTED_BLOCK_SIZES_COUNT 8 /** Intel's NVM DIMM format interface code. The current code should be 0x301 for AppDirect mode. Use the values that are reported by BIOS **/ #define DCPMM_FMT_CODE_APP_DIRECT 0x301 typedef struct _FIRMWARE_VERSION { UINT8 FwProduct; //!< FW Version Product Number UINT8 FwRevision; //!< FW Version Revision Number UINT8 FwSecurityVersion; //!< FW Version Security Version Number UINT8 FwApiMajor; //!< FW API Version Major UINT8 FwApiMinor; //!< FW API Version Minor UINT16 FwBuild; //!< FW Version Build Number } FIRMWARE_VERSION; /** SMBus PMem module address **/ typedef struct _SMBUS_DIMM_ADDR { UINT8 Cpu; UINT8 Imc; UINT8 Slot; } SMBUS_DIMM_ADDR; /*namespace mode fsdax or sector*/ #define NONE_MODE 0 #define FSDAX_MODE 1 #define SECTOR_MODE 2 // VFR compiler doesn't support typedef, that's why we use #define // A bitfield used for determining which PMem modules to work with for a given // operation. Used as input to VerifyTargetDimms() #define REQUIRE_DCPMMS UINT32 #define REQUIRE_DCPMMS_SELECT_ALL (0) // Allow all PMem modules #define REQUIRE_DCPMMS_MANAGEABLE (1 << 0) // See IsDimmManageable() for definition #define REQUIRE_DCPMMS_UNMANAGEABLE (1 << 1) // See IsDimmManageable() for definition #define REQUIRE_DCPMMS_FUNCTIONAL (1 << 2) // Currently vague, but means it initialized fully with no errors #define REQUIRE_DCPMMS_NON_FUNCTIONAL (1 << 3) // Generally means that DDRT is unusable, but can be non-functional for other reasons #define REQUIRE_DCPMMS_POPULATION_VIOLATION (1 << 4) // See IsDimmInPopulationViolation() for definition #define REQUIRE_DCPMMS_NO_POPULATION_VIOLATION (1 << 5) // See IsDimmInPopulationViolation() for definition #define REQUIRE_DCPMMS_NO_UNMAPPED_POPULATION_VIOLATION (1 << 6) // See IsDimmInUnmappedPopulationViolation() for definition #define REQUIRE_DCPMMS_MEDIA_ACCESSIBLE (1 << 7) // Select PMem modules where the media is accessible #define REQUIRE_DCPMMS_MEDIA_NOT_ACCESSIBLE (1 << 8) // Select PMem modules where the media is not accessible /* VFR compiler doesn't support typedef, that's why we use defines **/ #define DIMM_INFO_CATEGORIES UINT16 ///< @ref DIMM_INFO_CATEGORY_TYPES /** * @defgroup DIMM_INFO_CATEGORY_TYPES PMem Module Info Category Types * @{ */ #define DIMM_INFO_CATEGORY_NONE (0) ///< No DIMM_INFO fields will be populated #define DIMM_INFO_CATEGORY_RESERVED (1 << 0) ///< Reserved #define DIMM_INFO_CATEGORY_SECURITY (1 << 1) ///< Security fields will be populated: SecurityState. #define DIMM_INFO_CATEGORY_PACKAGE_SPARING (1 << 2) ///< Package sparing fields will be populated: PackageSparingEnabled, PackageSparesAvailable. #define DIMM_INFO_CATEGORY_ARS_STATUS (1 << 3) ///< ARS status field will be populated: ARSStatus. #define DIMM_INFO_CATEGORY_SMART_AND_HEALTH (1 << 4) ///< Health related fields will be populated: HealthStatusReason, LatchedLastShutdownStatus, LastShutdownTime, AitDramEnabled. #define DIMM_INFO_CATEGORY_POWER_MGMT_POLICY (1 << 5) ///< Power management fields will be populated: PeakPowerBudget, AvgPowerLimit, MemoryBandwidthBoostFeature, MemoryBandwidthBoostMaxPowerLimit, MemoryBandwidthBoostAveragePowerTimeConstant #define DIMM_INFO_CATEGORY_OPTIONAL_CONFIG_DATA_POLICY (1 << 6) ///< Optional config data policy fields will be populated: AvgPowerReportingTimeConstant #define DIMM_INFO_CATEGORY_OVERWRITE_DIMM_STATUS (1 << 7) ///< Overwrite PMem module status field will be populated: OverwriteDimmStatus. #define DIMM_INFO_CATEGORY_FW_IMAGE_INFO (1 << 8) ///< Firmware Image info fields will be populated: LastFwUpdateStatus, StagedFwVersion, FWImageMaxSize. #define DIMM_INFO_CATEGORY_MEM_INFO_PAGE_3 (1 << 9) ///< Memory info page 3 fields will be populated: ErrorInjectionEnabled, MediaTemperatureInjectionEnabled, SoftwareTriggersEnabled, PoisonErrorInjectionsCounter, PoisonErrorClearCounter, MediaTemperatureInjectionsCounter, SoftwareTriggersCounter, SoftwareTriggersEnabledDetails. #define DIMM_INFO_CATEGORY_VIRAL_POLICY (1 << 10) ///< Viral policy fields will be populated: ViralPolicyEnable, ViralStatus. #define DIMM_INFO_CATEGORY_DEVICE_CHARACTERISTICS (1 << 11) ///< Device Characteristics fields will be populated: CTST, MTST, MTSTT, MTSPT, CTSTT, CTSPT, MaxAveragePowerLimit, MaxMemoryBandwidthBoostMaxPowerLimit, MaxAveragePowerReportingTimeConstant, AveragePowerReportingTimeConstantStep. #define DIMM_INFO_CATEGORY_MEM_INFO_PAGE_4 (1 << 12) ///< Memory info page 4 fields will be populated #define DIMM_INFO_CATEGORY_EXTENDED_ADR (1 << 13) ///< Extended ADR status info #define DIMM_INFO_CATEGORY_LATCH_SYSTEM_SHUTDOWN_STATE (1 << 14) ///< Latch System Shutdown State fields will be populated: LatchSystemShutdownState, PreviousPowerCycleLatchSystemShutdownState #define DIMM_INFO_CATEGORY_ALL (0xFFFF) ///< All DIMM_INFO fields will be populated. /** * @} * DIMM_INFO_ERROR types */ #define DIMM_INFO_ERROR_NONE 0 #define DIMM_INFO_ERROR_UID (1 << 0) #define DIMM_INFO_ERROR_SECURITY_INFO (1 << 2) #define DIMM_INFO_ERROR_PACKAGE_SPARING (1 << 3) #define DIMM_INFO_ERROR_SMART_AND_HEALTH (1 << 4) #define DIMM_INFO_ERROR_POWER_MGMT (1 << 5) #define DIMM_INFO_ERROR_OPTIONAL_CONFIG_DATA (1 << 6) #define DIMM_INFO_ERROR_OVERWRITE_STATUS (1 << 7) #define DIMM_INFO_ERROR_CAPACITY (1 << 8) #define DIMM_INFO_ERROR_FW_IMAGE_INFO (1 << 9) #define DIMM_INFO_ERROR_VIRAL_POLICY (1 << 10) #define DIMM_INFO_ERROR_MEM_INFO_PAGE (1 << 11) #define DIMM_INFO_ERROR_MAX (1 << 12) #define DIMM_INFO_ERROR_DEVICE_CHARACTERISTICS (1 << 13) #define DIMM_INFO_ERROR_S3RESUME (1 << 14) #define DIMM_INFO_ERROR_SVN_DOWNGRADE (1 << 15) #define DIMM_INFO_ERROR_SECURE_ERASE_POLICY (1 << 16) #define DIMM_INFO_ERROR_FW_ACTIVATE (1 << 17) #define DIMM_INFO_ERROR_LATCH_SYSTEM_SHUTDOWN_STATE (1 << 18) #define DIMM_INFO_TYPE_CHAR16 1 #define DIMM_INFO_TYPE_UINT8 2 #define DIMM_INFO_TYPE_UINT16 3 #define DIMM_INFO_TYPE_UINT32 4 #define DIMM_INFO_TYPE_BOOLEAN 5 typedef struct { UINT64 Code; }DIMM_INFO_ATTRIBUTE_STATUS; typedef struct _DIMM_INFO_ATTRIB_HEADER { DIMM_INFO_ATTRIBUTE_STATUS Status; UINT8 Type; }DIMM_INFO_ATTRIB_HEADER; typedef struct _DIMM_INFO_ATTRIB_CHAR16{ DIMM_INFO_ATTRIB_HEADER Header; CHAR16 Data[32]; }DIMM_INFO_ATTRIB_CHAR16; typedef struct _DIMM_INFO_ATTRIB_UINT8 { DIMM_INFO_ATTRIB_HEADER Header; UINT8 Data; }DIMM_INFO_ATTRIB_UINT8; typedef struct _DIMM_INFO_ATTRIB_UINT16 { DIMM_INFO_ATTRIB_HEADER Header; UINT16 Data; }DIMM_INFO_ATTRIB_UINT16; typedef struct _DIMM_INFO_ATTRIB_UINT32 { DIMM_INFO_ATTRIB_HEADER Header; UINT32 Data; }DIMM_INFO_ATTRIB_UINT32; typedef struct _DIMM_INFO_ATTRIB_BOOLEAN { DIMM_INFO_ATTRIB_HEADER Header; BOOLEAN Data; } DIMM_INFO_ATTRIB_BOOLEAN; // The "global dimm struct" is at &gNvmDimmData->PMEMDev.Dimms and is populated // at HII driver loading, so they are included by default on any call to GetDimmInfo() // in NvmDimmConfig.c typedef struct _DIMM_INFO { // From global dimm struct UINT16 ManufacturerId; //!< Manufacturer number BOOLEAN ManufacturingInfoValid; //!< Validity of manufacturing location and date UINT8 ManufacturingLocation; //!< Manufacturing location UINT16 ManufacturingDate; //!< Manufacturing data UINT32 SerialNumber; //!< Serial number string CHAR16 PartNumber[PART_NUMBER_STR_LEN]; //!< Part number string CHAR16 DeviceLocator[DEVICE_LOCATOR_LEN]; //!< describing the physically-labeled socket or board position CHAR16 BankLabel[BANKLABEL_LEN]; //!< identifies the physically labeled bank FIRMWARE_VERSION FwVer; //!< FW version UINT16 DimmID; //!< SMBIOS Type 17 handle UINT16 SocketId; //!< socket id UINT16 InterfaceFormatCode[MAX_IFC_NUM]; //!< format interface codes UINT32 InterfaceFormatCodeNum; //!< number of format interface codes UINT8 FormFactor; //!< The PMem module form factor UINT16 DataWidth; //!< The width in bits used to store user data UINT16 TotalWidth; //!< The width in bits for data and error correction and/or data redundancy UINT16 Speed; //!< The speed in nanoseconds UINT64 CapacityFromSmbios; //!< The PMem module capacity from SMBIOS in bytes UINT64 Capacity; //!< PMem module capacity in bytes UINT64 VolatileCapacity; //!< Capacity in bytes mapped as volatile memory UINT64 PmCapacity; //!< Capacity in bytes reserved for persistent memory //DIMM_INFO_CATEGORY_SECURITY UINT8 SecurityState; //!< Identifies the security status of the PMem module collected from FW //DIMM_INFO_CATEGORY_PACKAGE_SPARING BOOLEAN PackageSparingCapable; //!< Whether or not the PMem module is capable of package sparing UINT8 PackageSparingEnabled; //!< Whether or not the package sparing policy is enabled on the PMem module UINT8 PackageSparesAvailable; //!< Whether or not the PMem module still has package spares available, //!< and the package spare has not yet been used by the package sparing policy; //!< this value will be 0 if the PMem module is not package sparing capable as per SKU //DIMM_INFO_CATEGORY_ARS_STATUS UINT8 ARSStatus; //!< Address Range Scrub (ARS) operation status for the PMem module //DIMM_INFO_CATEGORY_SMART_AND_HEALTH UINT8 HealthState; //!< Overall health state UINT16 HealthStatusReason; //!< Health state reason(s) UINT32 LatchedLastShutdownStatusDetails; //!< The detailed status of the last shutdown of the PMem module. UINT32 UnlatchedLastShutdownStatusDetails; //!< The detailed status of the last shutdown of the PMem module. UINT8 ThermalThrottlePerformanceLossPrct; //!< The average percentage loss (0..100) due to thermal throttling since last read in current boot. UINT64 LastShutdownTime; //!< The time the system was last shut down. UINT8 AitDramEnabled; //!< Whether or not the PMem module AIT DRAM is enabled. UINT16 MaxMediaTemperature; //!< The highest die temperature reported in degrees Celsius. UINT16 MaxControllerTemperature; //!< The highest controller temperature reported in degrees Celsius. //DIMM_INFO_CATEGORY_POWER_MGMT_POLICY DIMM_INFO_ATTRIB_UINT16 PeakPowerBudget; //!< The power budget in mW used for instantaneous power (10000-20000 mW). The default is 20000 mW. DIMM_INFO_ATTRIB_UINT16 AvgPowerLimit; //!< The power budget in mW used for average power (10000-18000 mW). The default is 12000 mW. DIMM_INFO_ATTRIB_UINT8 Reserved; DIMM_INFO_ATTRIB_UINT8 MemoryBandwidthBoostFeature; //!< Returns if the Memory Bandwidth Boost Mode is currently enabled or not. DIMM_INFO_ATTRIB_UINT16 MemoryBandwidthBoostMaxPowerLimit; //!< Power limit [mW] used for limiting the Memory Bandwidth Boost Mode power consumption. DIMM_INFO_ATTRIB_UINT32 MemoryBandwidthBoostAveragePowerTimeConstant; //!< The value used as a base time window for power usage measurements [ms]. //DIMM_INFO_CATEGORY_OPTIONAL_CONFIG_DATA_POLICY DIMM_INFO_ATTRIB_UINT8 Reserved2; DIMM_INFO_ATTRIB_UINT32 AvgPowerReportingTimeConstant; //!< The value [ms] used to determine the time constant for reporting average power consumption. //DIMM_INFO_CATEGORY_VIRAL_POLICY BOOLEAN ViralPolicyEnable; //!< True if viral policy is enabled BOOLEAN ViralStatus; //!< True if the status is viral // From global dimm struct UINT64 AppDirectCapacity; //!< Capacity in bytes mapped as persistent memory. UINT64 UnconfiguredCapacity; //!< Total PMem module capacity in bytes that needs further configuration. UINT64 ReservedCapacity; //!< Total PMem module capacity in bytes that is reserved for metadata. UINT64 InaccessibleCapacity; //!< Capacity in bytes for use that has not been exposed. //DIMM_INFO_CATEGORY_FW_IMAGE_INFO FIRMWARE_VERSION StagedFwVersion; //!< The current staged firmware version UINT32 FWImageMaxSize; //!< The maximum size of the Firmware UINT8 LastFwUpdateStatus; //!< Status of the last FW update //DIMM_INFO_CATEGORY_MEM_INFO_PAGE_3 BOOLEAN ErrorInjectionEnabled; //!< BIT 0 - Error injection is enabled BOOLEAN MediaTemperatureInjectionEnabled; //!< BIT 1 - Media temperature Injection is enabled BOOLEAN SoftwareTriggersEnabled; //!< BIT 2 - One of SW trigger is enabled UINT32 PoisonErrorInjectionsCounter; //!< This counter will be incremented each time the set poison error is successfully executed UINT32 PoisonErrorClearCounter; //!< This counter will be incremented each time the clear poison error is successfully executed UINT32 MediaTemperatureInjectionsCounter; //!< This counter will be incremented each time the media temperature is injected UINT32 SoftwareTriggersCounter; //!< This counter is incremented each time a software trigger is enabled UINT64 SoftwareTriggersEnabledDetails; //!< For each bit set, the corresponding trigger is currently enabled // From global dimm struct UINT8 ManageabilityState; //!< If the PMem module is manageable by this SW UINT8 IsNew; //!< If is incorporated with the rest of the PMem modules in the system UINT8 RebootNeeded; //!< Whether or not reboot is required to reconfigure PMem module UINT32 SkuInformation; //!< Information about SKU modes UINT16 VendorId; //!< Vendor ID UINT16 DeviceId; //!< Device ID UINT16 SubsystemVendorId; //!< Vendor ID of the subsystem memory controller UINT16 SubsystemDeviceId; //!< Device ID of the subsystem memory controller UINT16 Rid; //!< Revision ID UINT16 SubsystemRid; //!< Revision ID of the subsystem memory controller from NFIT UINT16 ImcId; //!< Memory controller ID UINT16 ChannelId; //!< Memory channel within an iMC UINT16 ChannelPos; //!< Position in the channel within an iMC UINT16 NodeControllerID; //!< The node controller identifier UINT8 MemoryType; //!< Memory type UINT8 ConfigStatus; //!< ConfigurationStatus code UINT8 ModesSupported; //!< A list of the modes supported by the PMem module BOOLEAN SecurityCapabilities; //!< The security features supported by the PMem module BOOLEAN SKUViolation; //!< The configuration of the PMem module is unsupported due to a license issue BOOLEAN IsInPopulationViolation; //!< The PMem module population falls outside of the supported config option UINT8 OverwriteDimmStatus; //!< Overwrite PMem module operation status BOOLEAN Configured; //!< True if the PMem module is configured CHAR16 ManufacturerStr[MANUFACTURER_LEN]; //!< Manufacturer string matched from manufacturer string number UINT32 DimmHandle; //!< The PMem module handle SMBUS_DIMM_ADDR SmbusAddress; //!< SMBUS address CHAR16 DimmUid[MAX_DIMM_UID_LENGTH]; //!< Globally unique NVDIMM ID (in hexadecimal format representation) UINT32 ErrorMask; //!< Bit mask representing which FW functions failed, see DIMM_INFO_ERROR types UINT16 ControllerRid; //!< Revision ID of the subsystem memory controller from FIS //DIMM_INFO_CATEGORY_DEVICE_CHARACTERISTICS DIMM_INFO_ATTRIB_UINT16 MaxAveragePowerLimit; //!< Maximum average power limit in increments of 250 [mW], if supported by the module. Else, 0x0. DIMM_INFO_ATTRIB_UINT16 MaxMemoryBandwidthBoostMaxPowerLimit; //!< Maximum value in increments of 250 [mW] that can be set via Set Power Management Policy, if supported by the module. Else, 0x0. DIMM_INFO_ATTRIB_UINT32 MaxMemoryBandwidthBoostAveragePowerTimeConstant; //!< Maximum supported value [ms] of the Memory Bandwidth Boost Average Power Time Constant. DIMM_INFO_ATTRIB_UINT32 MemoryBandwidthBoostAveragePowerTimeConstantStep; //!< Increments [ms] allowed by the FW when setting the Memory Bandwidth Boost Average Power Time Constant. DIMM_INFO_ATTRIB_UINT32 MaxAveragePowerReportingTimeConstant; //!< Maximum supported value [ms] of the Reporting Average Power Time Constant. DIMM_INFO_ATTRIB_UINT32 AveragePowerReportingTimeConstantStep; //!< Increments [ms] allowed by the FW when setting the Average Power Reporting Time Constant. //DIMM_INFO_CATEGORY_SECURITY BOOLEAN MasterPassphraseEnabled; //!< If 1, master passphrase is enabled UINT32 SecurityStateBitmask; UINT32 SVNDowngradeOptIn; UINT32 SecureErasePolicyOptIn; UINT32 S3ResumeOptIn; UINT32 FwActivateOptIn; CHAR16 SecurityStateStr[SECURITY_STATE_STR_LEN]; //DIMM_INFO_CATEGORY_MEM_INFO_PAGE_4 DIMM_INFO_ATTRIB_UINT16 DcpmmAveragePower;//!< PMem module Average Power DIMM_INFO_ATTRIB_UINT16 AveragePower12V; //!< Average 12V Power DIMM_INFO_ATTRIB_UINT16 AveragePower1_2V; //!< Average 1.2V Power //Extended ADR Status Info DIMM_INFO_ATTRIB_UINT8 ExtendedAdrEnabled; //!< Extended ADR flow enabled in the FW DIMM_INFO_ATTRIB_UINT8 PrevPwrCycleExtendedAdrEnabled; //!< Extended ADR flow enabled in the FW during the last power cycle //DIMM_INFO_CATEGORY_FW_IMAGE_INFO UINT8 StagedFwActivatable; //!< Specifies if the staged firmware is activatable UINT8 QuiesceRequired; //!< Specifies if FW Activate requires host to quiesce traffic before calling UINT16 ActivationTime; //!< Specifies activation time in ms for fw activate //DIMM_INFO_CATEGORY_LATCH_SYSTEM_SHUTDOWN_STATE UINT8 LatchSystemShutdownState; //!< Specifies whether latch is enabled UINT8 PrevPwrCycleLatchSystemShutdownState; //!< Specifies whether latch was enabled during the last power cycle //Fw Active Version UINT8 FwActiveApiVersionMajor; //!< Specifies the FW Active API major version UINT8 FwActiveApiVersionMinor; //!< Specifies the FW Active API minor version } DIMM_INFO; typedef struct _TOPOLOGY_DIMM_INFO { UINT8 PmttVersion; //!< PMTT Version UINT8 MemoryType; //!< memory type UINT16 DimmID; //!< SMBIOS Type 17 handle corresponding to this memory device UINT32 DimmHandle; //!< NFIT Device Handle UINT16 NodeControllerID; //!< Node Controller ID UINT16 SocketID; //!< Socket ID for the memory device UINT16 DieID; //!< die identifier UINT16 MemControllerID; //!< Memory Controller ID UINT16 ChannelID; //!< Channel identifier UINT16 SlotID; //!< Slot identifier UINT64 VolatileCapacity; //!< Capacity in bytes mapped as volatile memory CHAR16 DeviceLocator[DEVICE_LOCATOR_LEN]; //!< describing the physically-labeled socket or board position CHAR16 BankLabel[BANKLABEL_LEN]; //!< identifies the physically labeled bank } TOPOLOGY_DIMM_INFO; typedef struct _SOCKET_INFO { UINT16 SocketId; //!< Zero indexed processor identifier UINT64 MappedMemoryLimit; //!< Maximum amount of physical memory in bytes allowed to be mapped into SPA based on the SKU of the processor UINT64 TotalMappedMemory; //!< Total amount of physical memory in bytes currently mapped into the SPA for the processor } SOCKET_INFO; typedef struct _SYSTEM_CAPABILITIES_INFO { /** Bit0 If set BIOS supports changing configuration through management software. If clear BIOS does not allow configure change through management software Bit1 If set BIOS supports runtime interface to validate management configuration change request. Refer to BIOS runtime interface data structure. Note: this bit is valid only if Bit0 is set. **/ UINT8 PlatformConfigSupported; UINT16 InterleaveAlignmentSize; //!< Memory alignment size in bytes required when configuring pools UINT8 OperatingModeSupport; //!< Memory modes supported by BIOS UINT8 CurrentOperatingMode; //!< Memory modes (volatile and persistent) currently selected by BIOS UINT16 InterleaveFormatsSupportedNum; //!< Number of elements in list HII_POINTER PtrInterleaveFormatsSupported; //!< List of supported interleave set formats HII_POINTER PtrInterleaveSize; //!< PCAT 3.0 iMC and Channel interleave size UINT64 MinNsSize; //!< Minimum namespace size in bytes UINT64 NsBlockSizes[SUPPORTED_BLOCK_SIZES_COUNT]; //!< Supported namespace block sizes in bytes UINT8 AppDirectMirrorSupported; UINT8 DimmSpareSupported; UINT8 AppDirectMigrationSupported; UINT8 RenameNsSupported; UINT8 GrowPmNsSupported; UINT8 ShrinkPmNsSupported; UINT8 GrowBlkNsSupported; UINT8 ShrinkBlkNsSupported; UINT8 InitiateScrubSupported; UINT64 PartitioningAlignment; //!< The alignment for the create goal partition sizes in bytes UINT64 InterleaveSetsAlignment; BOOLEAN AdrSupported; BOOLEAN EraseDeviceDataSupported; BOOLEAN EnableDeviceSecuritySupported; BOOLEAN DisableDeviceSecuritySupported; BOOLEAN UnlockDeviceSecuritySupported; BOOLEAN FreezeDeviceSecuritySupported; BOOLEAN ChangeDevicePassphraseSupported; BOOLEAN ChangeMasterPassphraseSupported; BOOLEAN MasterEraseDeviceDataSupported; } SYSTEM_CAPABILITIES_INFO; typedef struct _MEMORY_RESOURCES_INFO { UINT64 RawCapacity; //!< Sum of the raw capacity on all PMem modules UINT64 VolatileCapacity; //!< Sum of the usable volatile capacity on all PMem modules UINT64 AppDirectCapacity; //!< Sum of the usable appdirect capacity on all PMem modules UINT64 UnconfiguredCapacity; //!< Sum of the PMem module capacity that is not configured UINT64 InaccessibleCapacity; //!< Sum of the PMem module capacity that is inaccessible due to a licensing issue UINT64 ReservedCapacity; //!< Sum of the capacity reserved for metadata on all PMem modules UINT64 DDRRawCapacity; //!< Sum of the raw capacity on all DDR DIMMs UINT64 DDRCacheCapacity; //!< Sum of the DDR capacity used for caching UINT64 DDRVolatileCapacity; //!< Sum of the DDR capacity used as volatile memory UINT64 DDRInaccessibleCapacity; //!< Sum of the DDR capacity that is inaccessible BOOLEAN PcdInvalid; //!< Boolean flag to indicate one or more PMem modules have missing or corrupt PCD UINT8 Reserved1[5]; UINT64 Reserved2[5]; } MEMORY_RESOURCES_INFO; // Hard to accidentally get 0xFFFFFFFE in practice (not 0xFFFFFFFF since BIOS is using // it for indicating no socket sku limit), so it's a reasonable unknown value indicator #define ACPI_TABLE_VALUE_UNKNOWN (MAX_UINT64_VALUE-1) typedef struct _DIMM_PERFORMANCE_DATA { UINT16 DimmId; //!< SMBIOS Type 17 handle corresponding to this memory device UINT128 MediaReads; //!< Number of 64-byte reads from media on the PMem module since last AC cycle UINT128 MediaWrites; //!< Number of 64-byte writes to media on the PMem module since last AC cycle UINT128 ReadRequests; //!< Number of DDRT read transactions the PMem module has serviced since last AC cycle UINT128 WriteRequests; //!< Number of DDRT write transactions the PMem module has serviced since last AC cycle UINT128 TotalMediaReads; //!< Lifetime number of 64-byte reads from media on the PMem module UINT128 TotalMediaWrites; //!< Lifetime number of 64-byte writes to media on the PMem module UINT128 TotalReadRequests; //!< Lifetime number of DDRT read transactions the PMem module has serviced UINT128 TotalWriteRequests; //!< Lifetime number of DDRT write transactions the PMem module has serviced // These are deprecated in the FIS, but leaving these in to preserve functionality // of manufacturing command (MfgShowPerformanceCommand.c). They are set to 0s // in GetDimmsPerformanceData UINT128 TotalBlockReadRequests; //!< Lifetime number of BW read requests the PMem module has serviced UINT128 TotalBlockWriteRequests; //!< Lifetime number of BW write requests the PMem module has serviced } DIMM_PERFORMANCE_DATA; /** Namespace information */ typedef struct _NAMESPACE_INFO { UINT8 NamespaceInfoNode[LIST_ENTRY_SIZE]; //!< Node (instead of LIST_ENTRY because of HII compilation issues) UINT64 Signature; //!< List signature UINT8 NamespaceGuid[NSGUID_LEN]; //!< UUID per RFC 4122 UINT16 NamespaceId; //!< Namespace ID UINT8 Name[NLABEL_NAME_LEN_WITH_TERMINATOR]; //!< Optional name 63 characters + (NULL-terminator) CHAR16 Reserved1[UINT128_DIGITS]; //!< Unused CHAR16 Reserved2[UINT128_DIGITS]; //!< Unused UINT16 HealthState; //!< Health state. Ok, Warning, Critical UINT16 RegionId; //!< ID of related region/IS UINT64 BlockSize; //!< Internal Block Size to calculate Capacity UINT64 LogicalBlockSize; //!< Logical Block Size from NS Label UINT64 BlockCount; //!< Block Count UINT8 NamespaceMode; //!< 0: None, 1: fsdax / pfn, 2: btt/sector UINT8 NamespaceType; //!< 1: Block, 2: PM UINT16 Major; //!< Namespace label major version UINT16 Minor; //!< Namespace label minor version UINT64 UsableSize; //!< Usable size of namespace, minus metadata } NAMESPACE_INFO; /** Default AppDirect Namespace block size **/ #define AD_NAMESPACE_BLOCK_SIZE 1 #define AD_NAMESPACE_LABEL_LBA_SIZE_512 512 #define AD_NAMESPACE_LABEL_LBA_SIZE_4K 4096 #define NAMESPACE_PURPOSE_LEN 64 #define NAMESPACE_INFO_SIGNATURE SIGNATURE_64('N', 'S', 'P', 'A', 'C', 'I', 'N', 'F') #define NAMESPACE_INFO_FROM_NODE(a) CR(a, NAMESPACE_INFO, NamespaceInfoNode, NAMESPACE_INFO_SIGNATURE) #define DIMM_PID_ALL 0xFFFF #define DIMM_PID_INVALID 0xFFFE #define DIMM_PID_NOTSET 0xFFFD #define REGION_ID_NOTSET 0xFFFD #define REGION_VOLATILE_SIZE_ALIGNMENT_B GIB_TO_BYTES(1ULL) #define REGION_PERSISTENT_SIZE_ALIGNMENT_B GIB_TO_BYTES(1ULL) /** Minimum size and alignment of AppDirect and Block Namespace is 1GB **/ #define PM_NAMESPACE_MIN_SIZE BYTES_IN_GIBIBYTE #define NAMESPACE_4KB_ALIGNMENT_SIZE KIB_TO_BYTES(4) #define NAMESPACE_64KB_ALIGNMENT_SIZE KIB_TO_BYTES (64) #define NAMESPACE_32GB_ALIGNMENT_SIZE GIB_TO_BYTES (32) /** Region Health States */ #define RegionHealthStateNormal 1 #define RegionHealthStateError 2 #define RegionHealthStateUnknown 3 #define RegionHealthStatePending 4 #define RegionHealthStateLocked 5 /** Region Interleave Format Types */ #define DEFAULT_INTERLEAVE_SET_TYPE 0 #define INTERLEAVED 1 #define NON_INTERLEAVED 2 /** Reserve Types */ #define RESERVE_DIMM_NONE 0 #define RESERVE_DIMM_AD_NOT_INTERLEAVED 2 #define DEFAULT_CHANNEL_INTERLEAVE_SIZE 0 #define DEFAULT_IMC_INTERLEAVE_SIZE 0 /* Recommended NM:FM ratio limits for Gen 1 and 2 */ #define TWOLM_NMFM_RATIO_LOWER_3_6 3.6 #define TWOLM_NMFM_RATIO_LOWER_3_6_STR L"3.6" #define TWOLM_NMFM_RATIO_UPPER_16 16 /* Recommended NM:FM ratio limits starting with Gen 3 */ #define TWOLM_NMFM_RATIO_LOWER_2 2.0 #define TWOLM_NMFM_RATIO_LOWER_2_STR L"2" #define TWOLM_NMFM_RATIO_UPPER_8 8 /* Region Information provides details about a PMEM region (interleave set).*/ typedef struct _REGION_INFO { UINT16 RegionId; ///< Region identifier UINT16 SocketId; ///< Socket identifier UINT8 RegionType; ///< Region type UINT64 Capacity; ///< Region total raw capacity UINT64 FreeCapacity; ///< Region total free capacity. Raw less capacity used by namespaces UINT64 AppDirNamespaceMaxSize; ///< Maximum size of an AppDirect namespace UINT64 AppDirNamespaceMinSize; ///< Minimum size of an AppDirect namespace UINT16 Health; ///< Health state of region UINT16 DimmId[12]; ///< PMem module IDs associated with this region UINT16 DimmIdCount; ///< Number of PMem modules found in DimmId UINT64 CookieId; ///< Interleave set ID HII_POINTER PtrInterleaveFormats; ///< Pointer to array of Interleave Formats UINT32 InterleaveFormatsNum; ///< Number of Interleave Formats } REGION_INFO; typedef struct _REGION_GOAL_TEMPLATE { UINT64 Size; //!< Size of the region in bytes UINT8 InterleaveSetType; //!< Type of interleave set: non-interleaved, interleaved BOOLEAN Asymmetrical; //!< Determine if region goal use asymmetrical config on socket } REGION_GOAL_TEMPLATE; /** Structure describes the usage characteristics and regions (interleave sets) of the specified PMem module */ typedef struct _REGION_GOAL_PER_DIMM_INFO { UINT32 DimmID; //!< PMem module ID CHAR16 DimmUid[MAX_DIMM_UID_LENGTH]; //!< PMem module UID UINT16 SocketId; //!< Socket ID that PMem module is found UINT32 PersistentRegions; //!< Count of persistent regions UINT64 VolatileSize; //!< Volatile capacity UINT8 NumberOfInterleavedDimms[MAX_IS_PER_DIMM]; //!< Count of PMem modules that are part of related Interleaved AppDirect regions UINT64 AppDirectSize[MAX_IS_PER_DIMM]; //!< AppDirect capacity UINT8 InterleaveSetType[MAX_IS_PER_DIMM]; //!< Type of interleave set: non-interleaved, interleaved UINT8 ImcInterleaving[MAX_IS_PER_DIMM]; //!< iMC interleaving as bit field UINT8 ChannelInterleaving[MAX_IS_PER_DIMM]; //!< Channel interleaving as bit field UINT8 AppDirectIndex[MAX_IS_PER_DIMM]; //!< AppDirect Index UINT8 Status; //!< Goal config status. See @ref GOAL_CONFIG_STATUS. } REGION_GOAL_PER_DIMM_INFO; #define MAX_ERROR_LOG_STRUCT_SIZE 64 /** Error Log Info */ typedef struct _ERROR_LOG_INFO { UINT16 DimmID; //!< PMem module ID of associated log info UINT64 SystemTimestamp; //!< Unix epoch time of log entry UINT8 ErrorType; //!< Error Log type. See @ref ERROR_LOG_TYPES. UINT8 OutputData[MAX_ERROR_LOG_STRUCT_SIZE]; //!< Error log data } ERROR_LOG_INFO; #define MAX_PAYLOAD_DEBUG_ENTRY_SIZE 128 typedef struct _DEBUG_LOG_INFO { UINT8 DimmID; UINT8 Data[MAX_PAYLOAD_DEBUG_ENTRY_SIZE]; //@Todo Discuss and diagnose how handle Large payload } DEBUG_LOG_INFO; /** Namespace related defines**/ #define UNKNOWN_TYPE_NAMESPACE 0 #define APPDIRECT_NAMESPACE 2 #define NAMESPACE_BLOCK_COUNT_UNDEFINED 0 #define NAMESPACE_PM_NAMESPACE_BLOCK_SIZE 1 #define NAMESPACE_PROPERTY_TRUE 1 #define NAMESPACE_PROPERTY_FALSE 0 #define IGNORE_YES_NO_VALUE_IGNORE 0 #define IGNORE_YES_NO_VALUE_YES 1 #define IGNORE_YES_NO_VALUE_NO 2 /** @defgroup HEALTH_STATUS Health Status @{ **/ #define HEALTH_UNKNOWN 0 ///< Unknown health status #define HEALTH_HEALTHY 1 ///< PMem module Healthy #define HEALTH_NON_CRITICAL_FAILURE 2 ///< Non-Critical (maintenance required) #define HEALTH_CRITICAL_FAILURE 3 ///< Critical (feature or performance degraded due to failure) #define HEALTH_FATAL_FAILURE 4 ///< Fatal (data loss has occurred or is imminent) #define HEALTH_UNMANAGEABLE 5 ///< PMem module is unmanageable #define HEALTH_NON_FUNCTIONAL 6 ///< PMem module is non-functional /** @} @defgroup HEALTH_STATUS_REASONS Health Status Reasons @{ **/ #define HEALTH_STATUS_REASON_NONE 0 ///< None #define HEALTH_REASON_PERCENTAGE_REMAINING_LOW BIT0 ///< Percentage remaining less than or equal to 1% #define HEALTH_REASON_PACKAGE_SPARING_HAS_HAPPENED BIT1 ///< Package sparing has happened #define HEALTH_REASON_CAP_SELF_TEST_WARNING BIT2 ///< CAP Self-Test returned a warning #define HEALTH_REASON_PERC_REMAINING_EQUALS_ZERO BIT3 ///< Percentage remaining is 0 #define HEALTH_REASON_DIE_FAILURE BIT4 ///< Die Failure (after package sparing if available) #define HEALTH_REASON_AIT_DRAM_DISABLED BIT5 ///< AIT DRAM state is disabled #define HEALTH_REASON_CAP_SELF_TEST_FAILURE BIT6 ///< CAP Self-Test failed #define HEALTH_REASON_CRITICAL_INTERNAL_STATE_FAILURE BIT7 ///< Critical internal state failure #define HEALTH_REASON_PERFORMANCE_DEGRADED BIT8 ///< Performance degraded #define HEALTH_REASON_CAP_SELF_TEST_COMM_FAILURE BIT9 ///< CAP Self Test Communication failure /** @} Security operations **/ #define SECURITY_OPERATION_UNDEFINED 0 #define SECURITY_OPERATION_SET_PASSPHRASE 1 #define SECURITY_OPERATION_CHANGE_PASSPHRASE 2 #define SECURITY_OPERATION_DISABLE_PASSPHRASE 3 #define SECURITY_OPERATION_UNLOCK_DEVICE 4 #define SECURITY_OPERATION_FREEZE_DEVICE 5 #define SECURITY_OPERATION_ERASE_DEVICE 6 #define SECURITY_OPERATION_CHANGE_MASTER_PASSPHRASE 7 #define SECURITY_OPERATION_MASTER_ERASE_DEVICE 8 /** Security states **/ #define SECURITY_UNKNOWN 0 #define SECURITY_DISABLED 1 #define SECURITY_LOCKED 2 #define SECURITY_UNLOCKED 3 #define SECURITY_FROZEN 4 #define SECURITY_PW_MAX 5 #define SECURITY_MASTER_PW_MAX 6 #define SECURITY_NOT_SUPPORTED 7 #define SECURITY_STATES_COUNT 8 #define SECURITY_MIXED_STATE 9 // Mixed security state in all PMem modules view /** Passphrase Type **/ #define SECURITY_USER_PASSPHRASE 0x0 #define SECURITY_MASTER_PASSPHRASE 0x1 /** Long operation status **/ #define LONG_OP_STATUS_UNKNOWN 0 #define LONG_OP_STATUS_NOT_STARTED 1 #define LONG_OP_STATUS_IN_PROGRESS 2 #define LONG_OP_STATUS_COMPLETED 3 #define LONG_OP_STATUS_ABORTED 4 #define LONG_OP_STATUS_ERROR 5 /** DDRT Training Status **/ #define DDRT_TRAINING_NOT_COMPLETE 0x00 #define DDRT_TRAINING_COMPLETE 0x01 #define DDRT_TRAINING_FAILURE 0x02 #define DDRT_S3_COMPLETE 0x03 #define NORMAL_MODE_COMPLETE 0x04 #define DDRT_TRAINING_UNKNOWN 0xFF /** PMem module Boot Status Bitmask **/ #define DIMM_BOOT_STATUS_NORMAL 0 #define DIMM_BOOT_STATUS_BSR_UNKNOWN BIT0 #define DIMM_BOOT_STATUS_MEDIA_NOT_READY BIT1 #define DIMM_BOOT_STATUS_MEDIA_ERROR BIT2 #define DIMM_BOOT_STATUS_MEDIA_DISABLED BIT3 #define DIMM_BOOT_STATUS_UNUSED BIT4 #define DIMM_BOOT_STATUS_DDRT_NOT_READY BIT5 #define DIMM_BOOT_STATUS_MAILBOX_NOT_READY BIT6 #define DIMM_BOOT_STATUS_REBOOT_REQUIRED BIT7 #define DIMM_BOOT_STATUS_SMBUS_NOT_READY BIT8 #define DIMM_BOOT_STATUS_INTERFACE_UNKNOWN BIT9 #define DIMM_BOOT_STATUS_UNKNOWN (DIMM_BOOT_STATUS_BSR_UNKNOWN | DIMM_BOOT_STATUS_INTERFACE_UNKNOWN) // Meta-flags #define DIMM_MEDIA_NOT_ACCESSIBLE(BootStatusBitmask) \ ((BootStatusBitmask & DIMM_BOOT_STATUS_MEDIA_NOT_READY) || \ (BootStatusBitmask & DIMM_BOOT_STATUS_MEDIA_ERROR) || \ (BootStatusBitmask & DIMM_BOOT_STATUS_MEDIA_DISABLED)) \ #define DIMM_DDRT_AND_SMBUS_INTERFACES_DOWN(BootStatusBitmask) ((BootStatusBitmask & DIMM_BOOT_STATUS_DDRT_NOT_READY) && \ (BootStatusBitmask & DIMM_BOOT_STATUS_SMBUS_NOT_READY)) #define RESET_DIMM_BOOT_STATUS_INTERFACE_BITS(BootStatusBitmask) \ do { \ BootStatusBitmask &= (~(DIMM_BOOT_STATUS_DDRT_NOT_READY | DIMM_BOOT_STATUS_SMBUS_NOT_READY | \ DIMM_BOOT_STATUS_INTERFACE_UNKNOWN)); \ } while (0) #define RESET_DIMM_BOOT_STATUS_BSR_BITS(BootStatusBitmask) \ do { \ BootStatusBitmask &= (~(DIMM_BOOT_STATUS_MEDIA_NOT_READY | DIMM_BOOT_STATUS_MEDIA_ERROR | \ DIMM_BOOT_STATUS_MEDIA_DISABLED | DIMM_BOOT_STATUS_MAILBOX_NOT_READY | \ DIMM_BOOT_STATUS_REBOOT_REQUIRED | DIMM_BOOT_STATUS_BSR_UNKNOWN)); \ } while (0) /** Security states bitmask **/ #define SECURITY_MASK_ENABLED BIT1 #define SECURITY_MASK_LOCKED BIT2 #define SECURITY_MASK_FROZEN BIT3 #define SECURITY_MASK_COUNTEXPIRED BIT4 #define SECURITY_MASK_NOT_SUPPORTED BIT5 #define SECURITY_MASK_MASTER_ENABLED BIT8 #define SECURITY_MASK_MASTER_COUNTEXPIRED BIT9 /** Security Opt-In values **/ #define S3_RESUME_SECURE_S3 0x0 #define S3_RESUME_UNSECURE_S3 0x1 #define SVN_DOWNGRADE_DISABLE 0x0 #define SVN_DOWNGRADE_ENABLE 0x1 #define SECURE_ERASE_NOT_OPTED_IN 0x0 #define SECURE_ERASE_OPTED_IN 0x1 #define FW_ACTIVATE_DISABLED 0x0 #define FW_ACTIVATE_ENABLED 0x1 #define OPT_IN_VALUE_INVALID 0xFF /** Form Factor **/ #define FORM_FACTOR_EMPTY 0 #define FORM_FACTOR_UNKNOWN 2 #define FORM_FACTOR_NVMDIMM 9 #define FORM_FACTOR_SODIMM 13 /** Supported memory modes by PCAT table 0 **/ #define SUPPORTED_ONE_LM_BIT 0 #define SUPPORTED_TWO_LM_BIT 1 #define SUPPORTED_PM_DIRECT_MODE_BIT 2 #define SUPPORTED_PM_CACHED_MODE_BIT 3 #define SUPPORTED_BLOCK_MODE_BIT 4 #define SUPPORTED_SUBNUMA_CLUSTER_BIT 5 /** Current memory mode by PCAT table 0 **/ #define CURRENT_1LM 0x1 #define CURRENT_2LM_PM_DIRECT 0x2 #define CURRENT_2LM_PM_CACHED 0x3 #define CURRENT_AUTO 0x4 // 2LM if DDR + Intel NVMDIMM with volatile mode present, 1LM otherwise /** Platform config with management software supported by PCAT table 0 **/ #define PLATFORM_CONFIG_SUPPORTED_BIT 0 #define RUNTIME_CHANGE_REQUEST_SUPPORT_BIT 1 /** Memory Type **/ #define MEMORYTYPE_UNKNOWN 0 #define MEMORYTYPE_DDR4 1 #define MEMORYTYPE_DCPM 2 #define MEMORYTYPE_DDR5 3 /** DIMM type **/ #define SMBIOS_MEMORY_TYPE_DDR4 0x1A #define SMBIOS_MEMORY_TYPE_DCPM 0x18 // kept for backward compatibility #define SMBIOS_MEMORY_TYPE_LOGICAL_NON_VOLATILE 0x1F /** SMBIOS Macros **/ #define SMBIOS_STR_UNKNOWN L"Unknown" #define SMBIOS_HANDLE_MASK MAX_UINT16 /** Package Sparing Capable **/ #define PACKAGE_SPARING_NOT_CAPABLE 0 #define PACKAGE_SPARING_CAPABLE 1 /** Package Sparing Enabled **/ #define PACKAGE_SPARING_DISABLED 0 #define PACKAGE_SPARING_ENABLED 1 /** Package Sparing Supported **/ #define PACKAGE_SPARING_NOT_SUPPORTED 0 #define PACKAGE_SPARING_SUPPORTED 1 /** Package Spares Available **/ #define PACKAGE_SPARES_NOT_AVAILABLE 0 #define PACKAGE_SPARES_AVAILABLE 1 /** Namespace Health Status **/ #define NAMESPACE_HEALTH_UNKNOWN 0 #define NAMESPACE_HEALTH_OK 1 #define NAMESPACE_HEALTH_WARNING 2 #define NAMESPACE_HEALTH_CRITICAL 3 #define NAMESPACE_HEALTH_UNSUPPORTED 4 #define NAMESPACE_HEALTH_LOCKED 5 /** Manageability states **/ #define MANAGEMENT_INVALID_CONFIG 0 #define MANAGEMENT_VALID_CONFIG 1 /** FW Logging Level **/ #define FW_LOG_LEVEL_DISABLED 0 #define FW_LOG_LEVEL_ERROR 1 #define FW_LOG_LEVEL_WARNING 2 #define FW_LOG_LEVEL_INFO 3 #define FW_LOG_LEVEL_DEBUG 4 #define OPTIONAL_DATA_UNDEFINED 0xFFUL // These values are now hard-coded into error strings in NvmDimmHii.uni for code simplicity. // Update STR_DCPMM_CONFIGURE_DATA_POLICY_VALUE_TOO_BIG_AVG_PWR_REPORTING_TIME_CONSTANT and friends // with any updated values. Main reason for going this way as opposed to appending onto the end // of an error string is that I wanted a thousands comma for readability: "12000" --> "12,000" #define AVG_PWR_REPORTING_TIME_CONSTANT_DEFAULT 1000 #define AVG_PWR_REPORTING_TIME_CONSTANT_MIN 100 #define AVG_PWR_REPORTING_TIME_CONSTANT_STEP 100 #define AVG_PWR_REPORTING_TIME_CONSTANT_MAX 12000 #define AVG_PWR_REPORTING_TIME_CONSTANT_MAX_STR_LEN 8 // Includes potential values of "Mixed" and "Unknown" /** Namespace security capabilities. **/ #define SECURITY_CAPABILITIES_ENCRYPTION_SUPPORTED BIT0 #define SECURITY_CAPABILITIES_ERASE_CAPABLE BIT1 /* VFR compiler doesn't support enums, that's why we use defines */ /** * @defgroup SENSOR_TYPES Sensor Types * Sensor IDs for the various sensor types * @{ */ #define SENSOR_TYPE_DIMM_HEALTH 0 ///< PMem module health sensor ID #define SENSOR_TYPE_MEDIA_TEMPERATURE 1 ///< Media temperature sensor ID #define SENSOR_TYPE_CONTROLLER_TEMPERATURE 2 ///< Controller temperature sensor ID #define SENSOR_TYPE_PERCENTAGE_REMAINING 3 ///< Percentage remaining sensor ID #define SENSOR_TYPE_LATCHED_DIRTY_SHUTDOWN_COUNT 4 ///< Latched dirty shutdowns count sensor ID #define SENSOR_TYPE_POWER_ON_TIME 5 ///< Power on time sensor ID #define SENSOR_TYPE_UP_TIME 6 ///< Up-time sensor ID #define SENSOR_TYPE_POWER_CYCLES 7 ///< Power cycles sensor ID #define SENSOR_TYPE_FW_ERROR_COUNT 8 ///< Firmware error count sensor ID #define SENSOR_TYPE_UNLATCHED_DIRTY_SHUTDOWN_COUNT 9 ///< Unlatched dirty shutdowns count sensor ID #define SENSOR_TYPE_ALL 10 ///< All sensor IDs #define SENSOR_TYPE_COUNT SENSOR_TYPE_ALL ///< Total count of all supported sensor types #define ALARM_COUNT 3 ///< Number of alarm threshold sensors /** @} */ /** Sensor enabled/disabled **/ #define SENSOR_DISABLED 0 ///< Sensor type disabled #define SENSOR_ENABLED 1 ///< Sensor type enabled #define SENSOR_NA_ENABLED 2 ///< Sensor type cannot be disabled/enabled /** show error **/ #define ERROR_LOG_DEFAULT_SEQUENCE_NUMBER 0 #define ERROR_LOG_MAX_SEQUENCE_NUMBER 65535 #define ERROR_LOG_MAX_COUNT 255 #define ERROR_LOG_DEFAULT_COUNT 1 /** dump FW debug log **/ #define MAX_LOG_PAGE_OFFSET 0xFFFFFFFF /** * @defgroup AIT_DRAM_STATUS AIT DRAM Status types * @{ */ #define AIT_DRAM_DISABLED 0 ///< ATI DRAM Disabled #define AIT_DRAM_ENABLED 1 ///< ATI DRAM Enabled /** * @} * Error injection Enabled states **/ #define ERR_INJ_DISABLED 0 #define ERR_INJ_ENABLED 1 /** Media temperature Enabled states **/ #define MEDIA_TEMP_DISABLED 0 #define MEDIA_TEMP_ENABLED 1 /** Software trigger Enabled states **/ #define SW_TRIGGER_DISABLED 0 #define SW_TRIGGER_ENABLED 1 /** 00 - Undefined 01 - PMem module is configured successfully 02 - Reserved 03 - All the PMem modules in the interleave set not found. Volatile memory is mapped to the SPA if possible. 04 - Persistent Memory not mapped due to matching Interleave set not found. Volatile memory is mapped to the SPA if possible. 05 - PMem module added to the system or moved within the system or PMem module is not yet configured. Volatile memory is mapped to the SPA if possible. Current configuration present in the PMem module is not modified (Reserved). 06 - New configuration input structures have errors, old configuration used. Refer to the config output structures for additional errors. 07 - New configuration input structures have errors. Volatile memory is mapped to the SPA if possible Refer to the config output structures for addition errors. 08 - Configuration Input Checksum not valid. 09 - Configuration Input data Revision is not supported. 10 - Current Configuration Checksum not valid. 11 - PMem module is not mapped to SPA due to a health issue or configuration change. 12 - PMem module persistent and volatile memory is not mapped due to a population issue. 13 - PMem module volatile memory is not mapped since NM:FM ratio is not supported. 14 - PMem module is not mapped due to a violation of the CPU maximum memory limit. 15 - PMem module persistent memory mapped, but volatile memory is not mapped due to a population issue. Other values reserved **/ #define DIMM_CONFIG_UNDEFINED 0 #define DIMM_CONFIG_SUCCESS 1 #define DIMM_CONFIG_RESERVED 2 #define DIMM_CONFIG_IS_INCOMPLETE 3 #define DIMM_CONFIG_NO_MATCHING_IS 4 #define DIMM_CONFIG_NEW_DIMM 5 #define DIMM_CONFIG_OLD_CONFIG_USED 6 #define DIMM_CONFIG_BAD_CONFIG 7 #define DIMM_CONFIG_IN_CHECKSUM_NOT_VALID 8 #define DIMM_CONFIG_REVISION_NOT_SUPPORTED 9 #define DIMM_CONFIG_CURR_CHECKSUM_NOT_VALID 10 #define DIMM_CONFIG_PM_NOT_MAPPED 11 #define DIMM_CONFIG_DCPMM_POPULATION_ISSUE 12 #define DIMM_CONFIG_DCPMM_NM_FM_RATIO_UNSUPPORTED 13 #define DIMM_CONFIG_CPU_MAX_MEMORY_LIMIT_VIOLATION 14 #define DIMM_CONFIG_PM_MAPPED_VM_POPULATION_ISSUE 15 /** 00 - Valid 01 - Not Configured 02 - Failed - Bad configuration 03 - Failed - Broken interleave 04 - Failed - Reverted 05 - Failed - Unsupported 06 - Failed - Partially supported **/ #define DIMM_INFO_CONFIG_VALID 0 #define DIMM_INFO_CONFIG_NOT_CONFIG 1 #define DIMM_INFO_CONFIG_BAD_CONFIG 2 #define DIMM_INFO_CONFIG_BROKEN_INTERLEAVE 3 #define DIMM_INFO_CONFIG_REVERTED 4 #define DIMM_INFO_CONFIG_UNSUPPORTED 5 #define DIMM_INFO_CONFIG_PARTIALLY_SUPPORTED 6 typedef struct { UINT8 Type; UINT8 State; UINT8 Enabled; UINT8 SettableThresholds; UINT8 SupportedThresholds; CHAR16 ValueString[HII_SENSOR_VALUE_STRING_SIZE]; UINT64 Value; UINT64 AlarmThreshold; UINT8 AlarmThresholdIsMixed; UINT64 NewAlarmThreshold; UINT64 ThrottlingStopThreshold; UINT64 ThrottlingStartThreshold; UINT64 ShutdownThreshold; UINT64 MaxTemperature; } DIMM_SENSOR_HII; typedef struct { UINT8 Enabled[MAX_DIMMS]; }DIMM_ALARM_ENABLED; #define DIMM_SENSOR_ALARM_DISABLED 0 #define DIMM_SENSOR_ALARM_ENABLED 1 #define DISPLAY_DIMM_ID_HANDLE 0 //!< Use SMBIOS Type 17 handle #define DISPLAY_DIMM_ID_UID 1 //!< Use PMem module UID #define DISPLAY_DIMM_ID_MAX_SIZE 2 //!< Number of possible PMem module Identifiers #define DISPLAY_DIMM_ID_DEFAULT DISPLAY_DIMM_ID_HANDLE #define APP_DIRECT_SETTINGS_INDEX_DEFAULT 0 #define BMI_DEFAULT MODE_DISABLED //!< default setting for boot minimal init #define DISPLAY_SIZE_UNIT_AUTO 0 //!< Automatically use the best format for each capacity in binary format #define DISPLAY_SIZE_UNIT_AUTO_10 1 //!< Automatically use the best format for each capacity in decimal multiples of bytes #define DISPLAY_SIZE_UNIT_B 2 //!< Display all capacities in bytes #define DISPLAY_SIZE_UNIT_MB 3 //!< Display all capacities in megabytes #define DISPLAY_SIZE_UNIT_MIB 4 //!< Display all capacities in mebibytes #define DISPLAY_SIZE_UNIT_GB 5 //!< Display all capacities in gigabytes #define DISPLAY_SIZE_UNIT_GIB 6 //!< Display all capacities in gibibytes #define DISPLAY_SIZE_UNIT_TB 7 //!< Display all capacities in terabytes #define DISPLAY_SIZE_UNIT_TIB 8 //!< Display all capacities in tebibytes #define DISPLAY_SIZE_MAX_SIZE 9 //!< Number of possible size display options #define DISPLAY_SIZE_UNIT_UNKNOWN 10 //!< Unknown units #define DISPLAY_DIMM_ID_VARIABLE_NAME L"CLI_DEFAULT_DIMM_ID" #define DISPLAY_SIZE_VARIABLE_NAME L"CLI_DEFAULT_SIZE" /** Intel PMem module Config automatic provisioning **/ #define INTEL_DIMM_CONFIG_VARIABLE_NAME L"IntelDIMMConfig" #define INTEL_DIMM_CONFIG_REVISION 2 #define PROVISION_CAPACITY_MODE_MANUAL 0 #define PROVISION_CAPACITY_MODE_AUTO 1 #define MEMORY_SIZE_MAX_PERCENT 100 #define PM_TYPE_APPDIRECT 0 #define PM_TYPE_APPDIRECT_NOT_INTERLEAVED 1 #define PROVISION_NAMESPACE_MODE_MANUAL 0 #define PROVISION_NAMESPACE_MODE_AUTO 1 #define NAMESPACE_FLAG_NONE 0 #define NAMESPACE_FLAG_BTT 1 #define PROVISION_CAPACITY_STATUS_NEW_UNKNOWN 0 #define PROVISION_CAPACITY_STATUS_SUCCESS 1 #define PROVISION_CAPACITY_STATUS_ERROR 2 #define PROVISION_CAPACITY_STATUS_PENDING 3 #define PROVISION_CAPACITY_STATUS_PENDING_SECURITY_DISABLED 4 #define PROVISION_NAMESPACE_STATUS_NEW_UNKNOWN 0 #define PROVISION_NAMESPACE_STATUS_SUCCESS 1 #define PROVISION_NAMESPACE_STATUS_ERROR 2 /** GUID for IntelDimmConfig variables for automatic provisioning **/ #define INTEL_DIMM_CONFIG_VARIABLE_GUID \ { 0x76fcbfb6, 0x38fe, 0x41fd, {0x90, 0x1d, 0x16, 0x45, 0x31, 0x22, 0xf0, 0x35}} /** GUID for PBR variables for playback and record **/ #define INTEL_DIMM_PBR_VARIABLE_GUID \ { 0x98209ce9, 0x1de5, 0x4dec, {0x93, 0xaf, 0x15, 0x29, 0x98, 0x83, 0x36, 0x2f}} /** GUID for PBR tag ids **/ #define INTEL_DIMM_PBR_TAGID_VARIABLE_GUID \ { 0x543f9bc3, 0x9b50, 0x425b, {0xa4, 0xce, 0x1e, 0x73, 0x30, 0xc4, 0xcf, 0x74}} typedef struct _INTEL_DIMM_CONFIG { UINT8 Revision; UINT8 ProvisionCapacityMode; UINT8 MemorySize; UINT8 PMType; UINT8 ProvisionNamespaceMode; UINT8 NamespaceFlags; UINT8 ProvisionCapacityStatus; UINT8 ProvisionNamespaceStatus; UINT8 NamespaceLabelVersion; UINT8 Reserved[7]; } INTEL_DIMM_CONFIG; #define OUTPUT_NVM_XML 1 << 0 #define OUTPUT_ESX_XML 1 << 1 #define OUTPUT_ESX_XML_TABLE 1 << 2 #define OUTPUT_TEXT 1 << 3 #define OUTPUT_VERBOSE 1 << 4 #define OUTPUT_ALL 0xFFFFFFFF typedef struct _DISPLAY_PREFERENCES { UINT8 DimmIdentifier; //!< Default display of PMem module identifiers UINT16 SizeUnit; //!< Default display capacity unit UINT8 OutputTypeMask; } DISPLAY_PREFERENCES; #define OUTPUT_NVM_XML_OPTION_SET(DispPref) ((DispPref.OutputTypeMask & OUTPUT_NVM_XML) != 0) #define OUTPUT_ESX_XML_OPTION_SET(DispPref) ((DispPref.OutputTypeMask & OUTPUT_ESX_XML) != 0) #define OUTPUT_ESX_XML_TABLE_OPTION_SET(DispPref) ((DispPref.OutputTypeMask & OUTPUT_ESX_XML_TABLE) != 0) #define OUTPUT_TEXT_OPTION_SET(DispPref) ((DispPref.OutputTypeMask & OUTPUT_TEXT) != 0) #define OUTPUT_VERBOSE_OPTION_SET(DispPref) ((DispPref.OutputTypeMask & OUTPUT_VERBOSE) != 0) typedef struct _DRIVER_PREFERENCES { UINT8 ImcInterleaving; //!< IMC interleaving as bit field UINT8 ChannelInterleaving; //!< Channel interleaving as bit field UINT8 AppDirectGranularity; //!< Granularity of AppDirect memory when goals are created } DRIVER_PREFERENCES; #define HOST_SERVER_NAME_LEN 100 #define HOST_SERVER_OS_NAME_LEN 100 #define HOST_SERVER_OS_VERSION_LEN 100 typedef struct _HOST_SERVER_INFO { CHAR16 Name[HOST_SERVER_NAME_LEN]; CHAR16 OsName[HOST_SERVER_OS_NAME_LEN]; CHAR16 OsVersion[HOST_SERVER_OS_VERSION_LEN]; }HOST_SERVER_INFO; /** Command Access Policy reflects if a FW Op/Sub Opcode is restricted */ typedef struct _COMMAND_ACCESS_POLICY_ENTRY { UINT8 Opcode; //!< Opcode of the FW command UINT8 SubOpcode; //!< SubOpcode of the FW command UINT8 Restriction; //!< Code for mailbox restrictions } COMMAND_ACCESS_POLICY_ENTRY; /** Total number of diagnostic test types */ #define DIAGNOSTIC_TEST_COUNT 4 /** Start Diagnostic Command tests codes **/ #define DIAGNOSTIC_TEST_UNKNOWN (0) #define DIAGNOSTIC_TEST_QUICK (BIT0) #define DIAGNOSTIC_TEST_CONFIG (BIT1) #define DIAGNOSTIC_TEST_SECURITY (BIT2) #define DIAGNOSTIC_TEST_FW (BIT3) #define DIAGNOSTIC_TEST_ALL (DIAGNOSTIC_TEST_QUICK | DIAGNOSTIC_TEST_CONFIG | DIAGNOSTIC_TEST_SECURITY | DIAGNOSTIC_TEST_FW) /** Get FW Debug Log Source Definitions */ #define FW_DEBUG_LOG_SOURCE_MEDIA 0 #define FW_DEBUG_LOG_SOURCE_SRAM 1 #define FW_DEBUG_LOG_SOURCE_SPI 2 #define FW_DEBUG_LOG_SOURCE_MAX 2 #define NUM_FW_DEBUG_LOG_SOURCES 3 /** Defines for the ModifyPcdConfig API */ #define DELETE_PCD_CONFIG_LSA_MASK (BIT0) //!< Delete the namespace partition #define DELETE_PCD_CONFIG_CIN_MASK (BIT1) //!< Zero Configuration Input Data Size and Start Offset in the Configuration Header #define DELETE_PCD_CONFIG_COUT_MASK (BIT2) //!< Zero Configuration Output Data Size and Start Offset in the Configuration Header #define DELETE_PCD_CONFIG_CCUR_MASK (BIT3) //!< Zero Current Configuration Data Size and Start Offset in the Configuration Header #define DELETE_PCD_CONFIG_ALL_MASK (DELETE_PCD_CONFIG_LSA_MASK | DELETE_PCD_CONFIG_CIN_MASK | DELETE_PCD_CONFIG_COUT_MASK | DELETE_PCD_CONFIG_CCUR_MASK) ///< @ref DELETE_PCD_CONFIG_ALL_MASK /** Defines for Restriction values returned from get command access policy **/ #define COMMAND_ACCESS_POLICY_RESTRICTION_NONE 0 #define COMMAND_ACCESS_POLICY_RESTRICTION_BIOSONLY 1 #define COMMAND_ACCESS_POLICY_RESTRICTION_SMBUSONLY 2 #define COMMAND_ACCESS_POLICY_RESTRICTION_BIOSSMBUSONLY 3 #define COMMAND_ACCESS_POLICY_RESTRICTION_MGMTONLY 4 #define COMMAND_ACCESS_POLICY_RESTRICTION_MGMTBIOSONLY 5 #define COMMAND_ACCESS_POLICY_RESTRICTION_MGMTSMBUSONLY 6 #define COMMAND_ACCESS_POLICY_RESTRICTION_MGMTBIOSSMBUSONLY 7 #define COMMAND_ACCESS_POLICY_RESTRICTION_UNSUPPORTED 0xFE #define COMMAND_ACCESS_POLICY_RESTRICTION_INVALID 0xFF #endif /** _NVM_TYPES_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/NvmWorkarounds.h000066400000000000000000000040121440615110200215750ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /** Dynamic workarounds flag. Only in the debug version, we enable the runtime workarounds that can be set by an environmental variable in the shell (before loading the driver). **/ #if !defined(MDEPKG_NDEBUG) #define DYNAMIC_WA_ENABLE #endif /** While the BIOS image has still older UEFI driver integrated, there is a mismatch between the checksum that it is initializing and the current driver is. In this case we need to skip this check to allow our driver properly handle the LSA regions on the DIMM. **/ #define WA_SKIP_LSA_CHECKSUM_FAIL /** SMBios table doesn't contain valid serial/model numbers **/ #define WA_GARBAGE_IN_SMBIOS_NVM_DIMM_ENTRIES /** No NGNVM memory type in the SMBIOS specification yet. **/ #define WA_SMBIOS_SPEC_DOES_NOT_CONTAIN_NGNVM_MEM_TYPE_YET /** Clearing buffer shouldn't be needed. In normal situations it is excessive. In case of problems uncomment this define to enforce clearing large payload in every passthru command. **/ //#define WA_CLEAR_LARGE_PAYLOAD_IN_PASSTHRU /** Duplicate DMA commands sent to channel require adding a delay before any writes to large payload register. Value in microseconds. **/ #define WA_MEDIA_WRITES_DELAY 5000 /** Ignore partition size restriction for Nanocore environment **/ //#define WA_IGNORE_PARTITION_SIZE_RESTRICTION /** Increase the mailbox timeout timers **/ //#define WA_APPLY_LARGE_MAILBOX_TIMEOUTS /** Run "Update Firmware" via small payloads **/ //#define WA_UPDATE_FIRMWARE_VIA_SMALL_PAYLOAD /** This makes any reads on BlockIO to be made twice. **/ #define WA_BLOCK_IO_READ_TWICE /** This makes any reads on Large Payload Mailbox to be made twice. **/ #define WA_LARGE_PAYLOAD_READ_TWICE /** Make Mailbox writes post in the absence of WPQFlush **/ //#define WA_NO_WPQFLUSH /** Enable setting the local flag on namespace labels version 1.2 Some OSs not compatible with this flag **/ //#define WA_ENABLE_LOCAL_FLAG_ON_NS_LABEL_1_2 ipmctl-03.00.00.0485/DcpmPkg/common/Pbr.c000066400000000000000000001105301440615110200173170ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include "Pbr.h" #include "PbrDcpmm.h" #ifdef OS_BUILD #include "PbrOs.h" #else extern EFI_RUNTIME_SERVICES *gRT; STATIC EFI_STATUS PbrSerializeCtx(PbrContext *ctx, BOOLEAN Force); STATIC EFI_STATUS PbrDeserializeCtx(PbrContext * ctx); #endif //local helper function prototypes STATIC EFI_STATUS PbrCheckBufferIntegrity(PbrContext *ctx); STATIC EFI_STATUS PbrComposeSession(PbrContext *pContext, VOID **ppBufferAddress, UINT32 *pBufferSize); STATIC EFI_STATUS PbrDecomposeSession(PbrContext *pContext, VOID *pPbrImg, UINT32 PbrImgSize); STATIC EFI_STATUS PbrCreateSessionContext(PbrContext * ctx); STATIC UINT32 PbrPartitionCount(); STATIC EFI_STATUS PbrGetPartition(UINT32 Signature, PbrPartitionContext **ppPartition); STATIC EFI_STATUS PbrCopyChunks(VOID *pDest, UINT32 pDestSz, VOID *pSource, UINT32 pSourceSz); PbrContext gPbrContext; //used for setting volatile/non-volatile uefi variables extern EFI_GUID gIntelDimmPbrVariableGuid; extern EFI_GUID gIntelDimmPbrTagIdVariableguid; /** Adds data to the recording session @param[in] Signature: unique dword identifier that categorizes the data to be recorded @param[in] pData: Data to be recorded. If NULL, a zeroed data buffer is allocated. Useful, when used with ppData. @param[in] Size: Byte size of pData @param[in] Singleton: Only one data object associated with Signature. Data previously set will be overridden with this data object. @param[out] - ppData - May be NULL, otherwise will contain a pointer to the memory allocated in the recording buffer for this data object. Warning, this pointer is only guaranteed to be valid until the next call to this function. @retval EFI_SUCCESS on success **/ EFI_STATUS EFIAPI PbrSetData( IN UINT32 Signature, IN VOID *pData, IN UINT32 Size, IN BOOLEAN Singleton, OUT VOID **ppData, OUT UINT32 *pLogicalIndex ) { UINT32 CtxIndex = 0; EFI_STATUS ReturnCode = EFI_SUCCESS; PbrContext *pContext = PBR_CTX(); PbrPartitionLogicalDataItem *pDataItem = NULL; //find the partition associated input param Signature for (CtxIndex = 0; CtxIndex < MAX_PARTITIONS; ++CtxIndex) { //partition signature check if (Signature == pContext->PartitionContexts[CtxIndex].PartitionSig) { //caller wants the data object to be a singleton (only one logical data associated with this specific partition) if (Singleton) { //is the size previously allocated for this partition big enough? if (Size > pContext->PartitionContexts[CtxIndex].PartitionSize) { //no it isn't, let's free anything previously allocated if (pContext->PartitionContexts[CtxIndex].PartitionData) { FreePool(pContext->PartitionContexts[CtxIndex].PartitionData); } //allocate just enough to add our new singleton data object pDataItem = AllocateZeroPool(Size+sizeof(PbrPartitionLogicalDataItem)); if (NULL == pDataItem) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_DBG("Failed to allocate memory for partition buffer\n"); goto Finish; } pContext->PartitionContexts[CtxIndex].PartitionData = pDataItem; //update our internal context with the new partition size pContext->PartitionContexts[CtxIndex].PartitionSize = Size + sizeof(PbrPartitionLogicalDataItem); //now that we have memory allocated, let's copy caller data into it //note, caller has option to not provide data. if (pData) { PbrCopyChunks(pDataItem->Data, Size, pData, Size); } pContext->PartitionContexts[CtxIndex].PartitionLogicalDataCnt = 1; pContext->PartitionContexts[CtxIndex].PartitionCurrentOffset = Size + sizeof(PbrPartitionLogicalDataItem); pContext->PartitionContexts[CtxIndex].PartitionEndOffset = 0; //not used yet //individual data objects within a partition are signed generically as PBR_LOGICAL_DATA_SIG //only the partition itself contains the specific signature associated with the data (each data signature has a partition associated with it) pDataItem->Signature = PBR_LOGICAL_DATA_SIG; pDataItem->Size = Size; goto Finish; } else { pDataItem = (PbrPartitionLogicalDataItem*)(pContext->PartitionContexts[CtxIndex].PartitionData); pDataItem->Signature = PBR_LOGICAL_DATA_SIG; pDataItem->Size = Size; if (pData) { PbrCopyChunks(pDataItem->Data, pContext->PartitionContexts[CtxIndex].PartitionSize, pData, Size); } goto Finish; } } else { //allocate more memory if needed if (pContext->PartitionContexts[CtxIndex].PartitionCurrentOffset + (Size + sizeof(PbrPartitionLogicalDataItem)) > pContext->PartitionContexts[CtxIndex].PartitionSize) { pContext->PartitionContexts[CtxIndex].PartitionData = ReallocatePool(pContext->PartitionContexts[CtxIndex].PartitionSize, pContext->PartitionContexts[CtxIndex].PartitionSize + ((Size + sizeof(PbrPartitionLogicalDataItem)) * PARTITION_GROW_SZ_MULTIPLIER), pContext->PartitionContexts[CtxIndex].PartitionData); if (NULL == pContext->PartitionContexts[CtxIndex].PartitionData) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_DBG("Failed to allocate memory for partition buffer\n"); goto Finish; } pContext->PartitionContexts[CtxIndex].PartitionSize += ((Size + sizeof(PbrPartitionLogicalDataItem)) * PARTITION_GROW_SZ_MULTIPLIER); } pDataItem = (PbrPartitionLogicalDataItem*)((UINTN)pContext->PartitionContexts[CtxIndex].PartitionData + (UINTN)pContext->PartitionContexts[CtxIndex].PartitionCurrentOffset); pDataItem->Signature = PBR_LOGICAL_DATA_SIG; pDataItem->Size = Size; //now that we have memory allocated, let's copy caller data into it //note, caller has option to not provide data. if (pData) { PbrCopyChunks(pDataItem->Data, pContext->PartitionContexts[CtxIndex].PartitionSize - pContext->PartitionContexts[CtxIndex].PartitionCurrentOffset, pData, Size); } //keep track of how many data objects copied to each partition pContext->PartitionContexts[CtxIndex].PartitionLogicalDataCnt++; //next position to copy data to pContext->PartitionContexts[CtxIndex].PartitionCurrentOffset += (Size + sizeof(PbrPartitionLogicalDataItem)); } goto Finish; } //if we haven't found a previously allocated partition associated with Signature then create one else if (PBR_INVALID_SIG == pContext->PartitionContexts[CtxIndex].PartitionSig) { break; } } //Need to create a new partition if (CtxIndex == MAX_PARTITIONS) { NVDIMM_ERR("Ran out of PBR partition space. Increase MAX_PARTITIONS\n"); return EFI_OUT_OF_RESOURCES; } pContext->PartitionContexts[CtxIndex].PartitionSig = Signature; pContext->PartitionContexts[CtxIndex].PartitionSize = Singleton ? (Size + sizeof(PbrPartitionLogicalDataItem)) : (Size + sizeof(PbrPartitionLogicalDataItem))*PARTITION_GROW_SZ_MULTIPLIER; pContext->PartitionContexts[CtxIndex].PartitionLogicalDataCnt = 1; pContext->PartitionContexts[CtxIndex].PartitionCurrentOffset = 0; pContext->PartitionContexts[CtxIndex].PartitionEndOffset = 0; pContext->PartitionContexts[CtxIndex].PartitionData = pDataItem = AllocateZeroPool(pContext->PartitionContexts[CtxIndex].PartitionSize); if (NULL == pContext->PartitionContexts[CtxIndex].PartitionData) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_DBG("Failed to allocate memory for partition buffer\n"); goto Finish; } pDataItem->Signature = PBR_LOGICAL_DATA_SIG; pDataItem->Size = Size; //now that we have a new partition and memory allocated, let's copy caller data into it //note, caller has option to not provide data. if (pData) { PbrCopyChunks(pDataItem->Data, pContext->PartitionContexts[CtxIndex].PartitionSize - sizeof(PbrPartitionLogicalDataItem), pData, Size); } //advance next recording offset pointer pContext->PartitionContexts[CtxIndex].PartitionCurrentOffset += (Size + sizeof(PbrPartitionLogicalDataItem)); Finish: //if no errors, and user wants a pointer to the allocated recording location if (EFI_SUCCESS == ReturnCode && ppData) { *ppData = (VOID*)(&pDataItem->Data[0]); } //every data object recorded is wrapped in a logical data structure, which retains the data object index //within the partition if (EFI_SUCCESS == ReturnCode) { pDataItem->LogicalIndex = pContext->PartitionContexts[CtxIndex].PartitionLogicalDataCnt - 1; } //caller wants to know the data object index of this particular data object. if (EFI_SUCCESS == ReturnCode && pLogicalIndex) { *pLogicalIndex = pDataItem->LogicalIndex; } return ReturnCode; } /** Gets data from the playback session @param[in] Signature: Specifies which data type to get @param[in] Index: GET_NEXT_DATA_INDEX gets the next data object within the playback session. Otherwise, any positive value will result in getting the data object at position 'Index' (base 0). If data associated with Signature is a Singleton, use Index '0'. @param[out] ppData: Newly allocated buffer that contains the data object. Caller is responsible for freeing it. @param[out] pSize: Size in bytes of ppData. @param[out] pLogicalIndex: May be NULL, otherwise will contain the logical index of the data object. @retval EFI_SUCCESS on success **/ EFI_STATUS EFIAPI PbrGetData( IN UINT32 Signature, IN INT32 Index, OUT VOID **ppData, OUT UINT32 *pSize, OUT UINT32 *pLogicalIndex ) { UINT32 CtxIndex = 0; UINT32 PartitionCtxIndex = 0; EFI_STATUS ReturnCode = EFI_NOT_FOUND; PbrContext *pContext = PBR_CTX(); PbrPartitionLogicalDataItem *pDataItem = NULL; //find the partition associated input param Signature for (CtxIndex = 0; CtxIndex < MAX_PARTITIONS; ++CtxIndex) { //if signature matches if (Signature == pContext->PartitionContexts[CtxIndex].PartitionSig) { //caller wants the next data object within the playback session if(GET_NEXT_DATA_INDEX == Index){ //get the next logical data item pDataItem = (PbrPartitionLogicalDataItem *)((UINTN)pContext->PartitionContexts[CtxIndex].PartitionData + (UINTN)pContext->PartitionContexts[CtxIndex].PartitionCurrentOffset); //verify the data item is valid, if not return EFI_NOT_FOUND if (PBR_LOGICAL_DATA_SIG != pDataItem->Signature) { goto Finish; } //found it, now advance the current pbr offset so the next time this is called the next logical data item is returned pContext->PartitionContexts[CtxIndex].PartitionCurrentOffset += (sizeof(PbrPartitionLogicalDataItem) + pDataItem->Size); ReturnCode = EFI_SUCCESS; } else { //caller wants a specific indexed data item pDataItem = (PbrPartitionLogicalDataItem *)(pContext->PartitionContexts[CtxIndex].PartitionData); //iterate through all items while (PBR_LOGICAL_DATA_SIG == pDataItem->Signature) { if ((UINT32)Index == PartitionCtxIndex) { ReturnCode = EFI_SUCCESS; break; } pDataItem = (PbrPartitionLogicalDataItem *)((UINTN)pDataItem + (UINTN)pDataItem->Size + sizeof(PbrPartitionLogicalDataItem)); ++PartitionCtxIndex; } } //if indexed item was located, allocate memory and copy the data to the caller if (EFI_SUCCESS == ReturnCode) { *ppData = AllocateZeroPool(pDataItem->Size); if (NULL == *ppData) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_DBG("Failed to allocate memory for partition buffer\n"); goto Finish; } *pSize = pDataItem->Size; PbrCopyChunks(*ppData, *pSize, pDataItem->Data, pDataItem->Size); ReturnCode = EFI_SUCCESS; goto Finish; } } } Finish: //if caller has requested the data item index if (EFI_SUCCESS == ReturnCode && pLogicalIndex) { *pLogicalIndex = pDataItem->LogicalIndex; } return ReturnCode; } /** Gets information pertaining to playback data associated with a specific data type (Signature). @param[in] Signature: Specifies data type interested in @param[out] pTotalDataItems: Number of logical data items of this Signature type that are available in the currently active playback session. @param[out] pTotalDataSize: Total size in bytes of all data associated with Signature type @param[out] pCurrentPlaybackDataOffset: Points to the next data item of Signature type that will be returned when PbrGetData is called with GET_NEXT_DATA_INDEX. @retval EFI_SUCCESS on success **/ EFI_STATUS EFIAPI PbrGetDataPlaybackInfo( IN UINT32 Signature, OUT UINT32 *pTotalDataItems, OUT UINT32 *pTotalDataSize, OUT UINT32 *pCurrentPlaybackDataOffset ) { UINT32 CtxIndex = 0; EFI_STATUS ReturnCode = EFI_NOT_FOUND; PbrContext *pContext = PBR_CTX(); PbrPartitionContext *pPartition = NULL; for (CtxIndex = 0; CtxIndex < MAX_PARTITIONS; ++CtxIndex) { if (Signature == pContext->PartitionContexts[CtxIndex].PartitionSig) { pPartition = &pContext->PartitionContexts[CtxIndex]; *pTotalDataItems = pPartition->PartitionLogicalDataCnt; *pTotalDataSize = pPartition->PartitionSize; *pCurrentPlaybackDataOffset = pPartition->PartitionCurrentOffset; ReturnCode = EFI_SUCCESS; break; } } return ReturnCode; } /** Sets the playback/recording mode @param[in] PbrMode: 0x0 - Normal, 0x1 - Recording, 0x2 - Playback @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_NOT_READY if PbrMode is set to Playback and session isn't loaded **/ EFI_STATUS EFIAPI PbrSetMode( IN UINT32 PbrMode ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PbrContext *pContext = PBR_CTX(); //if playback, ensure a session has been loaded if (PBR_PLAYBACK_MODE == PbrMode) { if (NULL == pContext->PbrMainHeader) { return EFI_NOT_READY; } } //if normal, free buffers else if (PBR_NORMAL_MODE == PbrMode) { PbrFreeSession(); ZeroMem(pContext, sizeof(PbrContext)); } //set the desired mode PBR_SET_MODE(pContext, PbrMode); //mode has changed, ensure the context gets serialized #ifndef OS_BUILD ReturnCode = PbrSerializeCtx(pContext, TRUE); if (!EFI_ERROR(ReturnCode)) { PBR_SET_MODE(pContext, PbrMode); } else { NVDIMM_DBG("Failed to set PBR mode variable!\n"); } #endif return ReturnCode; } /** Gets the current playback/recording mode @param[out] pPbrMode: 0x0 - Normal, 0x1 - Recording, 0x2 - Playback @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_NOT_READY if the pbr context is not available @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS EFIAPI PbrGetMode( OUT UINT32 *pPbrMode ) { PbrContext *pContext = PBR_CTX(); if (NULL == pPbrMode) { return EFI_INVALID_PARAMETER; } *pPbrMode = PBR_GET_MODE(pContext); return EFI_SUCCESS; } /** Set the PBR session buffer to use @param[in] pBufferAddress: address of a buffer to use for playback or record mode. If NULL new buffers will be created. @param[in] BufferSize: size in bytes of the buffer @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_NOT_READY if the pbr context is not available **/ EFI_STATUS EFIAPI PbrSetSession( IN VOID *pBufferAddress, IN UINT32 BufferSize ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PbrContext *pContext = PBR_CTX(); NVDIMM_DBG("PbrSetSession: Addr: 0x%x, Size: %d\n", (UINTN)pBufferAddress, BufferSize); //frees any existing session buffers ReturnCode = PbrFreeSession(); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to free session!"); goto Finish; } //caller wants to create a new session if (NULL == pBufferAddress) { ReturnCode = PbrCreateSessionContext(pContext); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to create a new buffer!"); goto Finish; } } else { //unravels PBR image and updates the context ReturnCode = PbrDecomposeSession(pContext, pBufferAddress, BufferSize); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to unstitch img!"); goto Finish; } } NVDIMM_DBG("About to do integrity check...\n"); ReturnCode = PbrCheckBufferIntegrity(pContext); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Invalid PBR Buffer!"); goto Finish; } NVDIMM_DBG("Done with integrity check\n"); //created a new session, make sure to serialize the context ReturnCode = PbrSerializeCtx(pContext, TRUE); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to set PBR Context variable\n"); goto Finish; } Finish: return ReturnCode; } /** Get the PBR Buffer that is current being used @param[out] ppBufferAddress: address to the pbr buffer @param[out] pBufferSize: size in bytes of the buffer @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS EFIAPI PbrGetSession( IN VOID **ppBufferAddress, IN UINT32 *pBufferSize ) { PbrContext *pContext = PBR_CTX(); EFI_STATUS ReturnCode = EFI_SUCCESS; if (NULL == ppBufferAddress || NULL == pBufferSize) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } //the session at this point is just a bunch of buffers, //PbrComposeSession stitches all of the buffers into a contiguous format... //something that can be loaded and decomposed in the future ReturnCode = PbrComposeSession(pContext, ppBufferAddress, pBufferSize); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to stitch image\n"); goto Finish; } Finish: return ReturnCode; } /** Free the PBR buffers that are currently being used @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS EFIAPI PbrFreeSession( ) { UINT32 CtxIndex = 0; PbrContext *pContext = PBR_CTX(); for (CtxIndex = 0; CtxIndex < MAX_PARTITIONS; ++CtxIndex) { if (PBR_INVALID_SIG != pContext->PartitionContexts[CtxIndex].PartitionSig) { if (pContext->PartitionContexts[CtxIndex].PartitionData) { FreePool(pContext->PartitionContexts[CtxIndex].PartitionData); } pContext->PartitionContexts[CtxIndex].PartitionSig = PBR_INVALID_SIG; } } FREE_POOL_SAFE(pContext->PbrMainHeader); return EFI_SUCCESS; } /** Reset all playback buffers to align with the specified TagId @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS EFIAPI PbrResetSession( IN UINT32 TagId ) { Tag *pTag = NULL; UINT32 DataSize = 0; PbrContext *pContext = PBR_CTX(); EFI_STATUS ReturnCode = EFI_SUCCESS; TagPartitionInfo *pTagPartitions = NULL; UINT32 CtxIndex = 0; UINT32 TagPartIndex = 0; //get the actual tag data item //this will contain offsets for all data partitions that existed when the tag was set/created ReturnCode = PbrGetData( PBR_TAG_SIG, TagId, (VOID**)&pTag, &DataSize, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("No TAG ID!\n"); goto Finish; } //immediately following the tag struct is a series of TagPartitionInfo objects //where each object describes one data partition pTagPartitions = (TagPartitionInfo*)((UINTN)pTag + sizeof(Tag)); //need to reset each data partition to the offset specified in the tag for (CtxIndex = 0; CtxIndex < MAX_PARTITIONS; ++CtxIndex) { //found a partition if (PBR_INVALID_SIG != pContext->PartitionContexts[CtxIndex].PartitionSig) { //default is to reset the current offset to the beginning pContext->PartitionContexts[CtxIndex].PartitionCurrentOffset = 0; //find the corresponding TagPartitionInfo associated with the partition signature for (TagPartIndex = 0; TagPartIndex < pTag->PartitionInfoCnt; ++TagPartIndex) { if (pTagPartitions[TagPartIndex].PartitionSignature == pContext->PartitionContexts[CtxIndex].PartitionSig) { pContext->PartitionContexts[CtxIndex].PartitionCurrentOffset = pTagPartitions[TagPartIndex].PartitionCurrentOffset; break; } } } } Finish: FREE_POOL_SAFE(pTag); return ReturnCode; } /** Set a tag associated with the current recording buffer offset @param[in] Signature: Signature associated with the tag @param[in] pName: name associated with the tag @param[in] pDescription: description of the tab @param[out] pId: logical ID given to the tag (will be appended to the name) @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS EFIAPI PbrSetTag( IN UINT32 Signature, IN CHAR16 *pName, IN CHAR16 *pDescription, OUT UINT32 *pId ) { UINT32 NewTagSize = 0; UINT32 DescriptionSize = 0; UINT32 NameSize = 0; CHAR8 *TagStrings = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; PbrContext *pContext = PBR_CTX(); Tag *pTagData = NULL; UINT32 LogicalIndex = 0; UINT32 PartitionCount = 0; UINT32 Index = 0; TagPartitionInfo *pTagPartitionInfo = NULL; if (PBR_RECORD_MODE != pContext->PbrMode) { return ReturnCode; } if (NULL == pName || NULL == pDescription) { NVDIMM_DBG("Invalid input params\n"); return EFI_INVALID_PARAMETER; } //predetermine the full tag size DescriptionSize = (UINT32)(StrLen(pDescription) + 1) * sizeof(CHAR8); NameSize = (UINT32)(StrLen(pName) + 1) * sizeof(CHAR8); NewTagSize = sizeof(Tag); NewTagSize += (DescriptionSize + NameSize); //each partition gets a TagPartitionInfo PartitionCount = PbrPartitionCount(); NewTagSize += (PartitionCount * sizeof(TagPartitionInfo)); //reserve a chunk of buffer within the TAG partition //to be filled in later in this function ReturnCode = PbrSetData( PBR_TAG_SIG, NULL, NewTagSize, FALSE, (VOID**)&pTagData, &LogicalIndex); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed to set tag data\n"); goto Finish; } //basic tag info pTagData->Signature = PBR_TAG_SIG; pTagData->TagSignature = Signature; pTagData->TagId = LogicalIndex; pTagData->TagSize = NewTagSize; pTagData->PartitionInfoCnt = PartitionCount; //after the tag structure will be an array of TagPartitionInfo structs //one per partition pTagPartitionInfo = (TagPartitionInfo*)((UINTN)pTagData + sizeof(Tag)); //fill in the TagPartitionInfo structs for (Index = 0; Index < MAX_PARTITIONS; ++Index) { if (PBR_INVALID_SIG != pContext->PartitionContexts[Index].PartitionSig) { pTagPartitionInfo->PartitionSignature = pContext->PartitionContexts[Index].PartitionSig; pTagPartitionInfo->PartitionCurrentOffset = pContext->PartitionContexts[Index].PartitionCurrentOffset; ++pTagPartitionInfo; } } //lastly, are the tag description/name strings TagStrings = (CHAR8*)((UINTN)pTagData + sizeof(Tag) + (PartitionCount * sizeof(TagPartitionInfo))); UnicodeStrToAsciiStrS(pName, TagStrings, NameSize); TagStrings = (CHAR8*)((UINTN)pTagData + sizeof(Tag) + (PartitionCount * sizeof(TagPartitionInfo)) + NameSize); UnicodeStrToAsciiStrS(pDescription, TagStrings, DescriptionSize); //if caller wants to know the logical index of this particular tag within the tag partition if (NULL != pId) { *pId = LogicalIndex; } Finish: return ReturnCode; } /** Get the number of tags associated with the recording buffer @param[out] pCount: get the number of tags @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS EFIAPI PbrGetTagCount( OUT UINT32 *pCount ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PbrPartitionContext *pPartition = NULL; if (NULL == pCount) { return EFI_INVALID_PARAMETER; } PbrGetPartition(PBR_TAG_SIG, &pPartition); if (pPartition) { *pCount = pPartition->PartitionLogicalDataCnt; } else { *pCount = 0; } NVDIMM_DBG("Tag count: %d\n", *pCount); return ReturnCode; } /** Get tag info @param[in] pId: tag identification @param[out] pSignature: signature associated with the tag @param[out] pName: name associated with the tag @param[out] ppDescription: description of the tab @param[out] ppTagPartitionInfo: array of TagPartitionInfo structs @param[out] pTagPartitionCnt: number of items in pTagPartitionInfo All out pointers need to be freed by caller. @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS EFIAPI PbrGetTag( IN UINT32 Id, OUT UINT32 *pSignature, OUT CHAR16 **ppName, OUT CHAR16 **ppDescription, OUT VOID **ppTagPartitionInfo, OUT UINT32 *pTagPartitionCnt ) { Tag *pTag = NULL; CHAR8 *pTagStrs = NULL; UINT32 pTagStrsSize = 0; EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 DataSize = 0; if (NULL == ppName || NULL == ppDescription) { return EFI_INVALID_PARAMETER; } ReturnCode = PbrGetData( PBR_TAG_SIG, Id, (VOID**)&pTag, &DataSize, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("No TAG data!\n"); goto Finish; } *pSignature = pTag->TagSignature; pTagStrs = (CHAR8*)((UINTN)pTag + sizeof(Tag) + (pTag->PartitionInfoCnt * sizeof(TagPartitionInfo))); pTagStrsSize = (UINT32)((AsciiStrLen(pTagStrs) + 1) * sizeof(CHAR16)); *ppName = AllocateZeroPool(pTagStrsSize); SafeAsciiStrToUnicodeStr(pTagStrs, (UINT32)(AsciiStrLen(pTagStrs) + 1), *ppName); pTagStrs = (CHAR8*)((UINTN)pTagStrs + (UINTN)(AsciiStrLen(pTagStrs) + 1)); pTagStrsSize = (UINT32)((AsciiStrLen(pTagStrs) + 1) * sizeof(CHAR16)); *ppDescription = AllocateZeroPool(pTagStrsSize); SafeAsciiStrToUnicodeStr(pTagStrs, (UINT32)(AsciiStrLen(pTagStrs) + 1), *ppDescription); if (pTagPartitionCnt) { *pTagPartitionCnt = pTag->PartitionInfoCnt; } if (ppTagPartitionInfo && pTag->PartitionInfoCnt > 0) { *ppTagPartitionInfo = AllocateZeroPool(pTag->PartitionInfoCnt * sizeof(TagPartitionInfo)); if (NULL == *ppTagPartitionInfo) { ReturnCode = EFI_OUT_OF_RESOURCES; } PbrCopyChunks(*ppTagPartitionInfo, pTag->PartitionInfoCnt * sizeof(TagPartitionInfo), (VOID*)((UINTN)pTag + sizeof(Tag)), pTag->PartitionInfoCnt * sizeof(TagPartitionInfo)); } Finish: FREE_POOL_SAFE(pTag); return ReturnCode; } /** Initialize data structures associated with PBR @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS PbrInit( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PbrContext *pContext = PBR_CTX(); //initialize the context's mode property ReturnCode = PbrDeserializeCtx(pContext); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to retrieve PBR_MODE config value"); goto Finish; } NVDIMM_DBG("PbrInit PBR MODE: %d\n", pContext->PbrMode); NVDIMM_DBG("PbrInit DONE\n"); Finish: return ReturnCode; } /** Uninitialize data structures associated with PBR @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS PbrUninit( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PbrContext *pContext = PBR_CTX(); ReturnCode = PbrSerializeCtx(pContext, FALSE); //todo for OS free buffers in context #ifdef OS_BUILD PbrFreeSession(pContext); ZeroMem(pContext, sizeof(PbrContext)); #endif return ReturnCode; } #ifndef OS_BUILD //Implementation for OS in PbrOs.c /** Helper that restores the context. At this point the context is only saved to a volatile store. **/ EFI_STATUS PbrSerializeCtx( PbrContext *ctx, BOOLEAN Force) { UINTN VariableSize; EFI_STATUS ReturnCode = EFI_SUCCESS; if (NULL == ctx) { NVDIMM_DBG("ctx is null\n"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } VariableSize = sizeof(PbrContext); ReturnCode = SET_VARIABLE( PBR_CONTEXT_VAR, gIntelDimmPbrVariableGuid, VariableSize, (VOID*)ctx); Finish: return ReturnCode; } /** Helper that saves the context. At this point the context is only saved to a volatile store. **/ EFI_STATUS PbrDeserializeCtx( PbrContext *ctx) { PbrContext NewCtx; UINTN VariableSize; EFI_STATUS ReturnCode = EFI_SUCCESS; if (NULL == ctx) { NVDIMM_DBG("ctx is null\n"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } VariableSize = sizeof(PbrContext); ReturnCode = GET_VARIABLE( PBR_CONTEXT_VAR, gIntelDimmPbrVariableGuid, &VariableSize, &NewCtx); if (ReturnCode == EFI_NOT_FOUND) { NVDIMM_DBG("PBR_CTX param not found, setting to default value\n"); ctx->PbrMode = PBR_NORMAL_MODE; ReturnCode = EFI_SUCCESS; goto Finish; } else if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to retrieve PBR_MODE config value"); goto Finish; } PbrCopyChunks((VOID*)ctx, sizeof(PbrContext), &NewCtx, sizeof(PbrContext)); Finish: return ReturnCode; } #endif /** Helper that decomposes/unstitches a PBR session **/ STATIC EFI_STATUS PbrDecomposeSession( IN PbrContext *pContext, IN VOID *pPbrImg, IN UINT32 PbrImgSize ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PbrPartitionTable *pPartitionTable = NULL; PbrHeader *pPbrHeader = NULL; UINT32 PartitionIndex = 0; ZeroMem(pContext->PartitionContexts, sizeof(pContext->PartitionContexts)); //update context's file header pContext->PbrMainHeader = (PbrHeader*)AllocateZeroPool(sizeof(PbrHeader)); if (NULL == pContext->PbrMainHeader) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } PbrCopyChunks(pContext->PbrMainHeader, sizeof(PbrHeader), pPbrImg, sizeof(PbrHeader)); if (((PbrHeader*)pContext->PbrMainHeader)->Signature != PBR_HEADER_SIG) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("Invalid buffer contents, PBR master header not found!\n"); goto Finish; } pPbrHeader = (PbrHeader*)pContext->PbrMainHeader; pPartitionTable = (PbrPartitionTable *)&(pPbrHeader->PartitionTable); for (PartitionIndex = 0; PartitionIndex < MAX_PARTITIONS; ++PartitionIndex) { if (PBR_INVALID_SIG != pPartitionTable->Partitions[PartitionIndex].Signature) { pContext->PartitionContexts[PartitionIndex].PartitionSig = pPartitionTable->Partitions[PartitionIndex].Signature; pContext->PartitionContexts[PartitionIndex].PartitionSize = pPartitionTable->Partitions[PartitionIndex].Size; pContext->PartitionContexts[PartitionIndex].PartitionLogicalDataCnt = pPartitionTable->Partitions[PartitionIndex].LogicalDataCnt; pContext->PartitionContexts[PartitionIndex].PartitionCurrentOffset = 0; pContext->PartitionContexts[PartitionIndex].PartitionEndOffset = 0; pContext->PartitionContexts[PartitionIndex].PartitionData = AllocateZeroPool(pPartitionTable->Partitions[PartitionIndex].Size); if (NULL == pContext->PartitionContexts[PartitionIndex].PartitionData) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_DBG("Failed to allocate memory for partition buffer\n"); goto Finish; } PbrCopyChunks(pContext->PartitionContexts[PartitionIndex].PartitionData, pPartitionTable->Partitions[PartitionIndex].Size, (VOID*)((UINTN)pPbrImg + pPartitionTable->Partitions[PartitionIndex].Offset), pPartitionTable->Partitions[PartitionIndex].Size); } } Finish: return ReturnCode; } /** Helper that stitches together all buffers to make a full PBR image **/ STATIC EFI_STATUS PbrComposeSession( IN PbrContext *pContext, OUT VOID **ppBufferAddress, OUT UINT32 *pBufferSize ) { PbrHeader *pPbrMainHeader = NULL; UINT32 BufferSize = 0; UINT8 *pTemp = NULL; UINT32 CtxIndex = 0; if (NULL == pContext) { NVDIMM_DBG("No PBR context\n"); return EFI_NOT_READY; } if (NULL == ppBufferAddress || NULL == pBufferSize) { return EFI_INVALID_PARAMETER; } pPbrMainHeader = (PbrHeader *)pContext->PbrMainHeader; if (pPbrMainHeader == NULL) { return EFI_NOT_FOUND; } ZeroMem(&pPbrMainHeader->PartitionTable, sizeof(PbrPartitionTable)); BufferSize = sizeof(PbrHeader); for (CtxIndex = 0; CtxIndex < MAX_PARTITIONS; ++CtxIndex) { if (PBR_INVALID_SIG != pContext->PartitionContexts[CtxIndex].PartitionSig) { pPbrMainHeader->PartitionTable.Partitions[CtxIndex].Signature = pContext->PartitionContexts[CtxIndex].PartitionSig; pPbrMainHeader->PartitionTable.Partitions[CtxIndex].Size = pContext->PartitionContexts[CtxIndex].PartitionSize; pPbrMainHeader->PartitionTable.Partitions[CtxIndex].LogicalDataCnt = pContext->PartitionContexts[CtxIndex].PartitionLogicalDataCnt; pPbrMainHeader->PartitionTable.Partitions[CtxIndex].Offset = BufferSize; BufferSize += pContext->PartitionContexts[CtxIndex].PartitionSize; } } *ppBufferAddress = AllocateZeroPool(BufferSize); NVDIMM_DBG("StitchImg: buffersize = %d bytes\n", BufferSize); if (NULL != *ppBufferAddress) { pPbrMainHeader = (PbrHeader*)*ppBufferAddress; //copy the main pbr header to the buffer PbrCopyChunks(*ppBufferAddress, BufferSize, pContext->PbrMainHeader, sizeof(PbrHeader)); //advance past the main header, this will be copied at the end pTemp = (VOID*)((UINTN)(*ppBufferAddress) + (UINTN)sizeof(PbrHeader)); NVDIMM_DBG("Copying main header: %d bytes\n", sizeof(PbrHeader)); for (CtxIndex = 0; CtxIndex < MAX_PARTITIONS; ++CtxIndex) { if (PBR_INVALID_SIG != pContext->PartitionContexts[CtxIndex].PartitionSig) { PbrCopyChunks(pTemp, BufferSize, pContext->PartitionContexts[CtxIndex].PartitionData, pContext->PartitionContexts[CtxIndex].PartitionSize); pTemp += pContext->PartitionContexts[CtxIndex].PartitionSize; } } *pBufferSize = BufferSize; } else { return EFI_OUT_OF_RESOURCES; } return EFI_SUCCESS; } /** Helper that allocates new pbr buffers **/ STATIC EFI_STATUS PbrCreateSessionContext( PbrContext * ctx ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PbrHeader *header = NULL; //allocate memory for the main pbr header if (ctx->PbrMainHeader) { FreePool(ctx->PbrMainHeader); } header = ctx->PbrMainHeader = (PbrHeader*)AllocateZeroPool(sizeof(PbrHeader)); if (NULL == ctx->PbrMainHeader) { NVDIMM_DBG("Failed to create PBR header"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } header->Signature = PBR_HEADER_SIG; Finish: return ReturnCode; } /** Helper that verifies the contents of a pbr session. **/ STATIC EFI_STATUS PbrCheckBufferIntegrity( PbrContext *ctx ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PbrHeader *pMasterPbrHeader = (PbrHeader *)ctx->PbrMainHeader; if (NULL == pMasterPbrHeader) { ReturnCode = EFI_LOAD_ERROR; goto Finish; } if (PBR_HEADER_SIG != pMasterPbrHeader->Signature) { ReturnCode = EFI_COMPROMISED_DATA; NVDIMM_ERR("Pbr integrity check failed: Master header signature invalid\n"); goto Finish; } Finish: return ReturnCode; } /** Helper that provides the number of active data partitions **/ STATIC UINT32 PbrPartitionCount( ) { UINT32 Index = 0; PbrContext *pContext = PBR_CTX(); UINT32 PartitionCount = 0; for (Index = 0; Index < MAX_PARTITIONS; ++Index) { if (PBR_INVALID_SIG != pContext->PartitionContexts[Index].PartitionSig) { ++PartitionCount; } } return PartitionCount; } /** Helper that finds a specific partition and provides a reference to it **/ STATIC EFI_STATUS PbrGetPartition( IN UINT32 Signature, OUT PbrPartitionContext **ppPartition ) { UINT32 CtxIndex = 0; EFI_STATUS ReturnCode = EFI_NOT_FOUND; PbrContext *pContext = PBR_CTX(); for (CtxIndex = 0; CtxIndex < MAX_PARTITIONS; ++CtxIndex) { if (Signature == pContext->PartitionContexts[CtxIndex].PartitionSig) { *ppPartition = &pContext->PartitionContexts[CtxIndex]; ReturnCode = EFI_SUCCESS; } } return ReturnCode; } #define COPY_CHUNK_SZ_BYTES 1024 /** Helper that breaks up large memory copies into manageable sized chunks **/ STATIC EFI_STATUS PbrCopyChunks( IN VOID *pDest, IN UINT32 pDestSz, IN VOID *pSource, IN UINT32 pSourceSz ) { UINT32 NumChunks = 0; UINT32 Index = 0; UINT32 Offset = 0; UINT32 LeftOverBytes = 0; NumChunks = pSourceSz / COPY_CHUNK_SZ_BYTES; for (Index = 0; Index < NumChunks; ++Index) { Offset = (Index* COPY_CHUNK_SZ_BYTES); CopyMem_S((VOID*)((UINTN)pDest + Offset), COPY_CHUNK_SZ_BYTES, (VOID*)((UINTN)pSource + Offset), COPY_CHUNK_SZ_BYTES); } LeftOverBytes = pSourceSz % COPY_CHUNK_SZ_BYTES; if (LeftOverBytes) { CopyMem_S((VOID*)((UINTN)pDest + pSourceSz - LeftOverBytes), LeftOverBytes, (VOID*)((UINTN)pSource + pSourceSz - LeftOverBytes), LeftOverBytes); } return EFI_SUCCESS; } ipmctl-03.00.00.0485/DcpmPkg/common/Pbr.h000066400000000000000000000150731440615110200173320ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _PBR_H_ #define _PBR_H_ #include #include /** Gets data from the playback session @param[in] Signature: Specifies which data type to get @param[in] Index: GET_NEXT_DATA_INDEX gets the next data object within the playback session. Otherwise, any positive value will result in getting the data object at position 'Index' (base 0). If data associated with Signature is a Singleton, use Index '0'. @param[out] ppData: Newly allocated buffer that contains the data object. Caller is responsible for freeing it. @param[out] pSize: Size in bytes of ppData. @param[out] pLogicalIndex: May be NULL, otherwise will contain the logical index of the data object. @retval EFI_SUCCESS on success **/ EFI_STATUS EFIAPI PbrGetData( IN UINT32 Signature, IN INT32 Index, OUT VOID **ppData, OUT UINT32 *pSize, OUT UINT32 *pLogicalIndex ); /** Adds data to the recording session @param[in] Signature: unique dword identifier that categorizes the data to be recorded @param[in] pData: Data to be recorded. If NULL, a zeroed data buffer is allocated. Useful, when used with ppData. @param[in] Size: Byte size of pData @param[in] Singleton: Only one data object associated with Signature. Data previously set will be overridden with this data object. @param[out] - ppData - May be NULL, otherwise will contain a pointer to the memory allocated in the recording buffer for this data object. Warning, this pointer is only guaranteed to be valid until the next call to this function. @retval EFI_SUCCESS on success **/ EFI_STATUS EFIAPI PbrSetData( IN UINT32 Signature, IN VOID *pData, IN UINT32 Size, IN BOOLEAN Singleton, OUT VOID **ppData, OUT UINT32 *pLogicalIndex ); /** Gets information pertaining to playback data associated with a specific data type (Signature). @param[in] Signature: Specifies data type interested in @param[out] pTotalDataItems: Number of logical data items of this Signature type that are available in the currently active playback session. @param[out] pTotalDataSize: Total size in bytes of all data associated with Signature type @param[out] pCurrentPlaybackDataOffset: Points to the next data item of Signature type that will be returned when PbrGetData is called with GET_NEXT_DATA_INDEX. @retval EFI_SUCCESS on success **/ EFI_STATUS EFIAPI PbrGetDataPlaybackInfo( IN UINT32 Signature, OUT UINT32 *pTotalDataItems, OUT UINT32 *pTotalDataSize, OUT UINT32 *pCurrentPlaybackDataOffset ); /** Sets the playback/recording mode @param[in] PbrMode: 0x0 - Normal, 0x1 - Recording, 0x2 - Playback @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS EFIAPI PbrSetMode( IN UINT32 PbrMode ); /** Gets the current playback/recording mode @param[out] pPbrMode: 0x0 - Normal, 0x1 - Recording, 0x2 - Playback @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS EFIAPI PbrGetMode( OUT UINT32 *pPbrMode ); /** Set the PBR session buffer to use @param[in] pBufferAddress: address of a buffer to use for playback or record mode @param[in] BufferSize: size in bytes of the buffer @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS EFIAPI PbrSetSession( IN VOID *pBufferAddress, IN UINT32 BufferSize ); /** Get the PBR Buffer that is current being used @param[out] ppBufferAddress: address to the pbr buffer @param[out] pBufferSize: size in bytes of the buffer @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS EFIAPI PbrGetSession( IN VOID **ppBufferAddress, IN UINT32 *pBufferSize ); /** Clear the dynamic portions of the PBR Buffer that is current being used @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS EFIAPI PbrFreeSession( ); /** Reset all playback buffers to align with the specified TagId @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS EFIAPI PbrResetSession( IN UINT32 TagId ); /** Set a tag associated with the current recording buffer offset @param[in] Signature: Signature associated with the tag @param[in] pName: name associated with the tag @param[in] pDescription: description of the tab @param[out] pId: logical ID given to the tag (will be appended to the name) @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS EFIAPI PbrSetTag( IN UINT32 Signature, IN CHAR16 *pName, IN CHAR16 *pDescription, OUT UINT32 *pId ); /** Get the number of tags associated with the recording buffer @param[out] pCount: get the number of tags @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS EFIAPI PbrGetTagCount( OUT UINT32 *pCount ); /** Get tag info @param[in] pId: tag identification @param[out] pSignature: signature associated with the tag @param[out] pName: name associated with the tag @param[out] ppDescription: description of the tab @param[out] ppTagPartitionInfo: array of TagPartitionInfo structs @param[out] pTagPartitionCnt: number of items in pTagPartitionInfo All out pointers need to be freed by caller. @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS EFIAPI PbrGetTag( IN UINT32 Id, OUT UINT32 *pSignature, OUT CHAR16 **ppName, OUT CHAR16 **ppDescription, OUT VOID **ppTagPartitionInfo, OUT UINT32 *pTagPartitionCnt ); /** Initialize data structures associated with PBR @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS PbrInit( ); /** Uninitialize data structures associated with PBR @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS PbrUninit( ); #endif //_PBR_H_ ipmctl-03.00.00.0485/DcpmPkg/common/PbrDcpmm.c000066400000000000000000000227641440615110200203130ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include "Pbr.h" #include "PbrDcpmm.h" /** Return the current FW_CMD from the playback buffer @param[in] pContext: Pbr context @param[in] pCmd: current FW_CMD from the playback buffer @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS PbrGetPassThruRecord( IN PbrContext *pContext, OUT NVM_FW_CMD *pCmd, OUT EFI_STATUS *pPassThruRc ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PbrPassThruReq *ptReq; PbrPassThruResp *ptResp; VOID *pData = NULL; UINT32 DataSize = 0; UINT32 CurDataPos = 0; if (PBR_PLAYBACK_MODE != pContext->PbrMode) { return EFI_SUCCESS; } ReturnCode = PbrGetData( PBR_PASS_THRU_SIG, GET_NEXT_DATA_INDEX, &pData, &DataSize, NULL); if (EFI_SUCCESS != ReturnCode) { Print(L"Failed to get data!!!!\n"); return ReturnCode; } ptReq = (PbrPassThruReq *)pData; if (pCmd->Opcode != ptReq->Opcode) { NVDIMM_ERR("Get Passthru Opcode mismatch, expected 0x%x, received 0x%x\n", pCmd->Opcode, ptReq->Opcode); ReturnCode = EFI_LOAD_ERROR; goto Finish; } if (pCmd->SubOpcode != ptReq->SubOpcode) { NVDIMM_ERR("Get Passthru SubOpcode mismatch, expected 0x%x, received 0x%x\n", pCmd->SubOpcode, ptReq->SubOpcode); ReturnCode = EFI_LOAD_ERROR; goto Finish; } //skip past the pass through request header CurDataPos += sizeof(PbrPassThruReq); //verify we didn't run out of data if (CurDataPos > DataSize) { NVDIMM_ERR("Failed to skip past the pass through request\n"); ReturnCode = EFI_LOAD_ERROR; goto Finish; } CurDataPos += ptReq->InputPayloadSize; //verify we didn't run out of data if (CurDataPos > DataSize) { NVDIMM_ERR("Failed to skip past the InputPayload\n"); ReturnCode = EFI_LOAD_ERROR; goto Finish; } CurDataPos += ptReq->InputLargePayloadSize; //verify we didn't run out of data if (CurDataPos > DataSize) { NVDIMM_ERR("Failed to skip past the InputLargePayload\n"); ReturnCode = EFI_LOAD_ERROR; goto Finish; } //should be pointing to the response header ptResp = (PbrPassThruResp *)((UINTN)pData + (UINTN)CurDataPos); pCmd->Status = ptResp->Status; pCmd->OutputPayloadSize = ptResp->OutputPayloadSize; pCmd->LargeOutputPayloadSize = ptResp->OutputLargePayloadSize; *pPassThruRc = ptResp->PassthruReturnCode; //skip past the response header CurDataPos += sizeof(PbrPassThruResp); //verify we didn't run out of data if (CurDataPos > DataSize) { NVDIMM_ERR("Failed to skip past the PbrPassThruResp\n"); ReturnCode = EFI_LOAD_ERROR; goto Finish; } //there is an output payload if (ptResp->OutputPayloadSize) { CopyMem_S(pCmd->OutPayload, OUT_PAYLOAD_SIZE, (UINT8*)((UINTN)pData + (UINTN)CurDataPos), ptResp->OutputPayloadSize); } //skip past the response output payload CurDataPos += ptResp->OutputPayloadSize; //verify we didn't run out of data if (CurDataPos > DataSize) { NVDIMM_ERR("Failed to skip past the OutputPayload\n"); ReturnCode = EFI_LOAD_ERROR; goto Finish; } //there is a large output payload if (ptResp->OutputLargePayloadSize) { CopyMem_S(pCmd->LargeOutputPayload, OUT_MB_SIZE, (UINT8*)pData + CurDataPos, ptResp->OutputLargePayloadSize); } Finish: FREE_POOL_SAFE(pData); return ReturnCode; } /** Record a FW_CMD into the recording buffer @param[in] pContext: Pbr context @param[in] pCmd: current FW_CMD from the playback buffer @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS PbrSetPassThruRecord( IN PbrContext *pContext, OUT NVM_FW_CMD *pCmd, EFI_STATUS PassthruReturnCode ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PbrPassThruReq *ptReq = NULL; PbrPassThruResp *ptResp = NULL; VOID *pData = NULL; UINT32 DataSize = 0; if (NULL == pContext || NULL == pCmd) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (PBR_RECORD_MODE != pContext->PbrMode) { ReturnCode = EFI_SUCCESS; goto Finish; } DataSize += sizeof(PbrPassThruReq); DataSize += pCmd->InputPayloadSize; DataSize += pCmd->LargeInputPayloadSize; DataSize += sizeof(PbrPassThruResp); DataSize += pCmd->OutputPayloadSize; DataSize += pCmd->LargeOutputPayloadSize; CHECK_RESULT(PbrSetData( PBR_PASS_THRU_SIG, NULL, DataSize, FALSE, &pData, NULL), Finish); ptReq = (PbrPassThruReq*)pData; ptReq->DimmId = pCmd->DimmID; ptReq->Opcode = pCmd->Opcode; ptReq->SubOpcode = pCmd->SubOpcode; ptReq->TotalMilliseconds = 0xDEADBEEF;// GetCurrentMilliseconds(); ptReq->InputPayloadSize = pCmd->InputPayloadSize; ptReq->InputLargePayloadSize = pCmd->LargeInputPayloadSize; if (pCmd->InputPayloadSize) { CopyMem_S((VOID*)((UINTN)pData + (UINTN)sizeof(PbrPassThruReq)), DataSize - sizeof(PbrPassThruReq), pCmd->InputPayload, pCmd->InputPayloadSize); } if (pCmd->LargeInputPayloadSize) { CopyMem_S((VOID*)((UINTN)pData + (UINTN)sizeof(PbrPassThruReq) + (UINTN)pCmd->InputPayloadSize), DataSize - sizeof(PbrPassThruReq) - pCmd->InputPayloadSize, pCmd->LargeInputPayload, pCmd->LargeInputPayloadSize); } ptResp = (PbrPassThruResp*)((UINTN)pData + (UINTN)sizeof(PbrPassThruReq) + (UINTN)pCmd->InputPayloadSize + (UINTN)pCmd->LargeInputPayloadSize); ptResp->DimmId = pCmd->DimmID; ptResp->PassthruReturnCode = PassthruReturnCode; ptResp->Status = pCmd->Status; ptResp->TotalMilliseconds = 0x55AA55AA; ptResp->OutputPayloadSize = pCmd->OutputPayloadSize; ptResp->OutputLargePayloadSize = pCmd->LargeOutputPayloadSize; if (pCmd->OutputPayloadSize) { CopyMem_S((VOID*)((UINTN)pData + sizeof(PbrPassThruReq) + pCmd->InputPayloadSize + pCmd->LargeInputPayloadSize + sizeof(PbrPassThruResp)), DataSize - sizeof(PbrPassThruReq) - pCmd->InputPayloadSize - pCmd->LargeInputPayloadSize - sizeof(PbrPassThruResp), pCmd->OutPayload, pCmd->OutputPayloadSize); } if (pCmd->LargeOutputPayloadSize) { CopyMem_S((VOID*)((UINTN)pData + sizeof(PbrPassThruReq) + pCmd->InputPayloadSize + pCmd->LargeInputPayloadSize + sizeof(PbrPassThruResp) + pCmd->OutputPayloadSize), DataSize - sizeof(PbrPassThruReq) - pCmd->InputPayloadSize - pCmd->LargeInputPayloadSize - sizeof(PbrPassThruResp) - pCmd->OutputPayloadSize, pCmd->LargeOutputPayload, pCmd->LargeOutputPayloadSize); } Finish: return ReturnCode; } /** Return the current table from the playback buffer @param[in] pContext: Pbr context @param[in] TableType: 1-smbios, 2-nfit, 3-pcat, 4-pmtt @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS PbrGetTableRecord( IN PbrContext *pContext, IN UINT32 TableType, OUT VOID **ppTable, OUT UINT32 *pTableSize ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Signature = PBR_INVALID_SIG; NVDIMM_DBG("GetTableRecord...\n"); if (PBR_PLAYBACK_MODE != pContext->PbrMode) { return ReturnCode; } NVDIMM_DBG("GetTableRecord type: %d\n", TableType); //todo check signatures switch (TableType) { case PBR_RECORD_TYPE_SMBIOS: Signature = PBR_SMBIOS_SIG; break; case PBR_RECORD_TYPE_NFIT: Signature = PBR_NFIT_SIG; break; case PBR_RECORD_TYPE_PCAT: Signature = PBR_PCAT_SIG; break; case PBR_RECORD_TYPE_PMTT: Signature = PBR_PMTT_SIG; break; default: NVDIMM_DBG("Unknown table type: %d", TableType); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = PbrGetData( Signature, 0, ppTable, pTableSize, NULL ); if (ReturnCode != EFI_SUCCESS) { Print(L"Failed to GET smbios\n"); } else { NVDIMM_DBG("GetTableRecord type: %d, size: %d bytes\n", TableType, *pTableSize); } Finish: return ReturnCode; } /** Record a table into the recording buffer @param[in] pContext: Pbr context @param[in] TableType: 1-smbios, 2-nfit, 3-pcat, 4-pmtt @retval EFI_SUCCESS if the table was found and is properly returned. **/ EFI_STATUS PbrSetTableRecord( IN PbrContext *pContext, IN UINT32 TableType, IN VOID *pTable, IN UINT32 TableSize ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Signature = PBR_INVALID_SIG; NVDIMM_DBG("SetTableRecord...\n"); if (PBR_RECORD_MODE != pContext->PbrMode) { return ReturnCode; } NVDIMM_DBG("SetTableRecord type: %d\n", TableType); switch (TableType) { case PBR_RECORD_TYPE_SMBIOS: NVDIMM_DBG("Set Table Record: SMBIOS: Table size: %d\n", TableSize); Signature = PBR_SMBIOS_SIG; break; case PBR_RECORD_TYPE_NFIT: NVDIMM_DBG("Set Table Record: NFIT: Table size: %d\n", TableSize); Signature = PBR_NFIT_SIG; break; case PBR_RECORD_TYPE_PCAT: NVDIMM_DBG("Set Table Record: PCAT: Table size: %d\n", TableSize); Signature = PBR_PCAT_SIG; break; case PBR_RECORD_TYPE_PMTT: NVDIMM_DBG("Set Table Record: PMTT: Table size: %d\n", TableSize); Signature = PBR_PMTT_SIG; break; default: NVDIMM_DBG("Unknown table type: %d", TableType); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = PbrSetData( Signature, pTable, TableSize, TRUE, NULL, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed to set partition data (signature: %d)\n", Signature); goto Finish; } Finish: return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/common/PbrDcpmm.h000066400000000000000000000147001440615110200203070ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _PBR_DCPMM_H_ #define _PBR_DCPMM_H_ #include #include #include #define PBR_TMP_DIR "/tmp/pbr/" //DCPMM specific PBR defines #define PBR_RECORD_TYPE_SMBIOS 0x1 #define PBR_RECORD_TYPE_NFIT 0x2 #define PBR_RECORD_TYPE_PCAT 0x3 #define PBR_RECORD_TYPE_PMTT 0x4 #define PBR_RECORD_TYPE_PASSTHRU 0x5 #define PBR_DCPMM_CLI_SIG SIGNATURE_32('P', 'B', 'D', 'C') #define PBR_PASS_THRU_SIG SIGNATURE_32('P', 'B', 'C', 'L') #define PBR_UEFI_DRIVER_INIT_SIG SIGNATURE_32('P', 'B', 'D', 'I') #define PBR_SMBIOS_SIG SIGNATURE_32('P', 'B', 'S', 'M') #define PBR_NFIT_SIG SIGNATURE_32('P', 'B', 'N', 'F') #define PBR_PCAT_SIG SIGNATURE_32('P', 'B', 'P', 'C') #define PBR_PMTT_SIG SIGNATURE_32('P', 'B', 'P', 'M') #define PBR_FILE_DESCRIPTION "Intel(R) Optane(TM) DC Persistent Memory Recording File." #define PBR_DRIVER_INIT_TAG_DESCRIPTION L"driver: initialization" /**passthru data struct that is used within the passthru partition**/ typedef struct _PbrPassThruReq { UINT64 TotalMilliseconds; //!< Duration of the PT request UINT32 DimmId; //!< Target DIMM ID UINT8 Opcode; //!< FIS Opcode UINT8 SubOpcode; //!< FIS SubOpcode UINT32 InputPayloadSize; //!< FIS Input payload size (small payload) UINT32 InputLargePayloadSize; //!< FIS Input large payload size UINT8 Input[]; //!< Payload }PbrPassThruReq; /**passthru data struct that is used within the passthru partition**/ typedef struct _PbrPassThruResp { UINT64 TotalMilliseconds; //!< Duration of the PT response EFI_STATUS PassthruReturnCode; //!< Return value from the PT adapter layer UINT32 DimmId; //!< Target DIMM ID UINT32 OutputPayloadSize; //!< FIS Output payload size (small payload) UINT32 OutputLargePayloadSize; //!< FIS Output large payload size UINT8 Status; //!< FIS Status UINT8 Output[]; //!< Payload }PbrPassThruResp; /**smbios data struct that is used within the smbios partition**/ typedef struct _PbrSmbiosTableRecord { UINT32 Size; //!< Size of the smbios table UINT8 Minor; //!< Minor version of table UINT8 Major; //!< Major version of table UINT8 Table[]; //!< SMBIOS table(s) }PbrSmbiosTableRecord; /** Return the current FW_CMD from the playback buffer @param[in] pContext: Pbr context @param[in] pCmd: current FW_CMD from the playback buffer @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS PbrGetPassThruRecord( IN PbrContext *pContext, OUT NVM_FW_CMD *pCmd, OUT EFI_STATUS *pPassThruRc ); /** Record a FW_CMD into the recording buffer @param[in] pContext: Pbr context @param[in] pCmd: current FW_CMD from the playback buffer @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS PbrSetPassThruRecord( IN PbrContext *pContext, OUT NVM_FW_CMD *pCmd, EFI_STATUS PassthruReturnCode ); /** Return the current table from the playback buffer @param[in] pContext: Pbr context @param[in] TableType: 1-smbios, 2-nfit, 3-pcat, 4-pmtt @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS PbrGetTableRecord( IN PbrContext *pContext, IN UINT32 TableType, OUT VOID **ppTable, OUT UINT32 *pTableSize ); /** Record a table into the recording buffer @param[in] pContext: Pbr context @param[in] TableType: 1-smbios, 2-nfit, 3-pcat, 4-pmtt @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS PbrSetTableRecord( IN PbrContext *pContext, IN UINT32 TableType, IN VOID *pTable, IN UINT32 TableSize ); /** Return the current FW_CMD from the playback buffer @param[in] pContext: Pbr context @param[in] pCmd: current FW_CMD from the playback buffer @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS PbrGetPassThruRecord( IN PbrContext *pContext, OUT NVM_FW_CMD *pCmd, OUT EFI_STATUS *pPassThruRc ); /** Record a FW_CMD into the recording buffer @param[in] pContext: Pbr context @param[in] pCmd: current FW_CMD from the playback buffer @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS PbrSetPassThruRecord( IN PbrContext *pContext, OUT NVM_FW_CMD *pCmd, EFI_STATUS PassthruReturnCode ); /** Return the current table from the playback buffer @param[in] pContext: Pbr context @param[in] TableType: 1-smbios, 2-nfit, 3-pcat, 4-pmtt @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS PbrGetTableRecord( IN PbrContext *pContext, IN UINT32 TableType, OUT VOID **ppTable, OUT UINT32 *pTableSize ); /** Record a table into the recording buffer @param[in] pContext: Pbr context @param[in] TableType: 1-smbios, 2-nfit, 3-pcat, 4-pmtt @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. **/ EFI_STATUS PbrSetTableRecord( IN PbrContext *pContext, IN UINT32 TableType, IN VOID *pTable, IN UINT32 TableSize ); #endif //_PBR_DCPMM_H_ ipmctl-03.00.00.0485/DcpmPkg/common/PbrOs.c000066400000000000000000000201761440615110200176270ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include "PbrOs.h" #include "PbrDcpmm.h" #include #include #include #ifdef _MSC_VER #include #include #include #include #include extern int registry_volatile_write(const char *key, unsigned int dword_val); extern int registry_read(const char *key, unsigned int *dword_val, unsigned int default_val); #else #include #include #include #include #include #include #include #define _read read #define _getch getchar #endif #define PBR_CTX_FILE_NAME "pbr_ctx.tmp" #define PBR_MAIN_FILE_NAME "pbr_main.tmp" #define FILE_READ_OPTS "rb" #define FILE_WRITE_OPTS "wb" VOID SerializePbrMode(UINT32 mode); VOID DeserializePbrMode(UINT32 *pMode, UINT32 defaultMode); /**Memory buffer serialization**/ #define SerializeBuffer(file, buffer, size) \ if (0 != os_fopen(&pFile, file, FILE_WRITE_OPTS)) \ { \ NVDIMM_ERR("Failed to open the PBR file: %s\n", file); \ ReturnCode = EFI_NOT_FOUND; \ goto Finish; \ } \ BytesWritten = fwrite(buffer, size, 1, pFile); \ if (1 != BytesWritten) \ { \ NVDIMM_ERR("Failed to serialize the PBR file: %s\n", file); \ ReturnCode = EFI_END_OF_FILE; \ goto Finish; \ } \ if (pFile) { \ fclose(pFile); \ pFile = NULL; \ } /**Memory buffer deserialization**/ #define DeserializeBuffer(file, buffer, size) \ if (0 != os_fopen(&pFile, file, FILE_READ_OPTS)) \ { \ NVDIMM_ERR("Failed to open the PBR file: %s\n", file); \ ReturnCode = EFI_END_OF_FILE; \ goto Finish; \ } \ fseek(pFile, 0L, SEEK_END); \ fseek(pFile, 0L, SEEK_SET); \ buffer = malloc(size); \ if(NULL == buffer) { \ NVDIMM_ERR("Failed to allocate memory for deserializing buffer\n"); \ ReturnCode = EFI_OUT_OF_RESOURCES; \ goto Finish; \ } \ if (1 != fread(buffer, size, 1, pFile)) \ { \ NVDIMM_ERR("Failed to read the PBR file: %s\n", file); \ ReturnCode = EFI_END_OF_FILE; \ goto Finish; \ } \ if (pFile) { \ fclose(pFile); \ pFile = NULL; \ } #define DeserializeBufferEx(file, buffer, size) \ if (0 != os_fopen(&pFile, file, FILE_READ_OPTS)) \ { \ NVDIMM_ERR("Failed to open the PBR file: %s\n", file); \ } \ else { \ fseek(pFile, 0L, SEEK_END); \ fseek(pFile, 0L, SEEK_SET); \ buffer = malloc(size); \ if(NULL == buffer) { \ NVDIMM_ERR("Failed to allocate memory for deserializing buffer\n"); \ } \ if (1 != fread(buffer, size, 1, pFile)) \ { \ NVDIMM_ERR("Failed to read the PBR file: %s\n", file); \ } \ if (pFile) { \ fclose(pFile); \ pFile = NULL; \ } \ } /** Helper that restores the context. Note, effort has been taken to NOT preserve the session pbr mode across boots. **/ EFI_STATUS PbrSerializeCtx( PbrContext *ctx, BOOLEAN Force ) { EFI_STATUS ReturnCode = EFI_SUCCESS; FILE* pFile = NULL; size_t BytesWritten = 0; char pbr_dir[100]; char pbr_filename[100]; UINT32 CtxIndex = 0; if (NULL == ctx) { NVDIMM_DBG("ctx is null\n"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } //create temp directory (buffers serialized into files that reside here) AsciiSPrint(pbr_dir, sizeof(pbr_dir), PBR_TMP_DIR); os_mkdir(pbr_dir); SerializePbrMode(ctx->PbrMode); if (ctx->PbrMode == PBR_NORMAL_MODE && !Force) { return ReturnCode; } for (CtxIndex = 0; CtxIndex < MAX_PARTITIONS; ++CtxIndex) { if (PBR_INVALID_SIG != ctx->PartitionContexts[CtxIndex].PartitionSig) { AsciiSPrint(pbr_filename, sizeof(pbr_filename), "%x.pbr", ctx->PartitionContexts[CtxIndex].PartitionSig); AsciiSPrint(pbr_dir, sizeof(pbr_dir), "%s%s", PBR_TMP_DIR, pbr_filename); SerializeBuffer(pbr_dir, ctx->PartitionContexts[CtxIndex].PartitionData, ctx->PartitionContexts[CtxIndex].PartitionSize); } } /**Serialize the PBR context struct**/ SerializeBuffer(PBR_TMP_DIR PBR_CTX_FILE_NAME, ctx, sizeof(PbrContext)); /**Serialize the PBR main header**/ SerializeBuffer(PBR_TMP_DIR PBR_MAIN_FILE_NAME, ctx->PbrMainHeader, sizeof(PbrHeader)); Finish: if (pFile) { fclose(pFile); } return ReturnCode; } /** Helper that saves the context. Note, effort has been taken to NOT preserve the session pbr mode across boots. **/ EFI_STATUS PbrDeserializeCtx( PbrContext *ctx ) { EFI_STATUS ReturnCode = EFI_SUCCESS; FILE* pFile = NULL; UINT32 PbrMode = PBR_NORMAL_MODE; char pbr_dir[100]; char pbr_filename[100]; UINT32 CtxIndex = 0; if (NULL == ctx) { NVDIMM_DBG("ctx is null\n"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } //create temp directory (buffers serialized into files that reside here) AsciiSPrint(pbr_dir, sizeof(pbr_dir), PBR_TMP_DIR); os_mkdir(pbr_dir); DeserializePbrMode(&PbrMode, PBR_NORMAL_MODE); NVDIMM_DBG("PBR MODE from shared memory: %d\n", PbrMode); AsciiSPrint(pbr_filename, sizeof(pbr_filename), "%x.pbr", PBR_PASS_THRU_SIG); AsciiSPrint(pbr_dir, sizeof(pbr_dir), "%s%s", PBR_TMP_DIR, pbr_filename); /**Deserialize the PBR context struct**/ if (0 != os_fopen(&pFile, PBR_TMP_DIR PBR_CTX_FILE_NAME, FILE_READ_OPTS) || pFile == NULL) { NVDIMM_DBG("pbr_ctx.tmp not found, setting to default value\n"); ctx->PbrMode = PBR_NORMAL_MODE; ReturnCode = EFI_SUCCESS; goto Finish; } if (1 != fread(ctx, sizeof(PbrContext), 1, pFile)) { NVDIMM_ERR("Failed to read the PBR context\n"); ReturnCode = EFI_END_OF_FILE; goto Finish; } fclose(pFile); pFile = NULL; /**Deserialize the PBR main header**/ DeserializeBuffer(PBR_TMP_DIR PBR_MAIN_FILE_NAME, ctx->PbrMainHeader, sizeof(PbrHeader)); for (CtxIndex = 0; CtxIndex < MAX_PARTITIONS; ++CtxIndex) { if (PBR_INVALID_SIG != ctx->PartitionContexts[CtxIndex].PartitionSig) { AsciiSPrint(pbr_filename, sizeof(pbr_filename), "%x.pbr", ctx->PartitionContexts[CtxIndex].PartitionSig); AsciiSPrint(pbr_dir, sizeof(pbr_dir), "%s%s", PBR_TMP_DIR, pbr_filename); DeserializeBufferEx(pbr_dir, ctx->PartitionContexts[CtxIndex].PartitionData, ctx->PartitionContexts[CtxIndex].PartitionSize); } } ctx->PbrMode = PbrMode; Finish: if (pFile) { fclose(pFile); } return ReturnCode; } /** Helper that serializes pbr mode to a volatile store. We should not be maintaining sessions across system reboots **/ VOID SerializePbrMode( UINT32 mode ) { #if _MSC_VER registry_volatile_write("pbr_mode", mode); #else UINT32 ShmId; key_t Key; UINT32 *pPbrMode = NULL; Key = ftok(PBR_TMP_DIR, 'h'); ShmId = shmget(Key, sizeof(*pPbrMode), IPC_CREAT | 0666); if (-1 == ShmId) { NVDIMM_DBG("Failed to shmget\n"); return; } pPbrMode = (UINT32*)shmat(ShmId, NULL, 0); if ((VOID*)pPbrMode == (VOID*)-1) { NVDIMM_DBG("Failed to shmat\n"); } else { *pPbrMode = mode; NVDIMM_DBG("Writing to shared memory: %d\n", *pPbrMode); shmdt(pPbrMode); //If the mode was set to Normal Mode(0x0) //Then it is ok to mark it to be removed if (PBR_NORMAL_MODE == mode) { shmctl(ShmId, IPC_RMID, NULL); } } #endif } /** Helper that deserializes pbr mode from a volatile store. We should not be maintaining sessions across system reboots **/ VOID DeserializePbrMode( UINT32 *pMode, UINT32 defaultMode ) { #if _MSC_VER registry_read("pbr_mode", pMode, defaultMode); #else UINT32 ShmId; key_t Key; UINT32 *pPbrMode = NULL; Key = ftok(PBR_TMP_DIR, 'h'); ShmId = shmget(Key, sizeof(*pPbrMode), IPC_CREAT | 0666); if (-1 == ShmId) { NVDIMM_DBG("Failed to shmget\n"); return; } pPbrMode = (UINT32*)shmat(ShmId, NULL, 0); if ((VOID*)pPbrMode == (VOID*)-1) { NVDIMM_DBG("Failed to shmat\n"); *pMode = defaultMode; } else { *pMode = *pPbrMode; shmdt(pPbrMode); //If the mode was set to Normal Mode(0x0) //Then it is ok to mark it to be removed if (PBR_NORMAL_MODE == *pMode) { shmctl(ShmId, IPC_RMID, NULL); } } #endif } ipmctl-03.00.00.0485/DcpmPkg/common/PbrOs.h000066400000000000000000000004551440615110200176320ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _PBR_OS_H_ #define _PBR_OS_H_ #include #include EFI_STATUS PbrSerializeCtx(PbrContext *ctx, BOOLEAN Force); EFI_STATUS PbrDeserializeCtx(PbrContext * ctx); #endif //_PBR_OS_H_ ipmctl-03.00.00.0485/DcpmPkg/common/PbrTypes.h000066400000000000000000000126451440615110200203610ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _PBR_TYPES_H_ #define _PBR_TYPES_H_ #include //Generic PBR defines #define PBR_NORMAL_MODE 0x0 #define PBR_RECORD_MODE 0x1 #define PBR_PLAYBACK_MODE 0x2 #define GET_NEXT_DATA_INDEX -1 #define MAX_PARTITIONS 100 #define MAX_TAG_NAME 256 #define INVALID_TAG_ID 0xFFFFFFFF #define PARTITION_GROW_SZ_MULTIPLIER 10 #define PBR_SW_VERSION_MAX 25 #define PBR_OS_NAME_MAX 100 #define PBR_OS_VERSION_MAX 100 #define PBR_FILE_DESCRIPTION_MAX 1024 #define PBR_INVALID_SIG 0 #define PBR_LOGICAL_DATA_SIG SIGNATURE_32('P', 'B', 'L', 'D') #define PBR_HEADER_SIG SIGNATURE_32('P', 'B', 'R', 'H') #define PBR_TAG_HEADER_SIG SIGNATURE_32('P', 'B', 'T', 'H') #define PBR_TAG_SIG SIGNATURE_32('P', 'B', 'T', 'I') /**set playback/record/normal mode**/ #define PBR_SET_MODE(ctx, mode) \ (ctx)->PbrMode = mode /**get current playback/record/normal mode**/ #define PBR_GET_MODE(ctx) \ (ctx)->PbrMode /**obtain pointer to the playback/recording module context**/ #define PBR_CTX() \ &gPbrContext #pragma pack(push) #pragma pack(1) /**part of the main PbrContext struct, used to keep track on individual data partitions in the session**/ typedef struct _PbrPartitionContext { UINT32 PartitionSig; //!< Unique identifier that categorizes a set of data UINT32 PartitionSize; //!< Size in bytes of the partition UINT32 PartitionLogicalDataCnt; //!< How many logical data items exist in the partition UINT32 PartitionCurrentOffset; //!< Offset used to keep track of current position when in record or playback mode UINT32 PartitionEndOffset; //!< Placeholder VOID *PartitionData; //!< Pointer to actual data item }PbrPartitionContext; /**the main pbr context that contains pointers to various data structures**/ typedef struct _PbrContext { UINT32 PbrMode; //!< PBR_NORMAL_MODE, PBR_RECORD_MODE, PBR_PLAYBACK_MODE VOID *PbrMainHeader; //!< Main PBR buffer header, includes partition table PbrPartitionContext PartitionContexts[MAX_PARTITIONS]; }PbrContext; /**entries in the partition table**/ typedef struct _PbrPartitionTableEntry { UINT32 Signature; //!< Defines the type of partition UINT32 Size; //!< Size of partition including the partition header UINT32 Offset; //!< Offset of the partition within a fully stitched pbr image UINT32 LogicalDataCnt; //!< Number of logical data items within a partition }PbrPartitionTableEntry; typedef struct _PbrPartitionLogicalDataItem { UINT32 Signature; //!< PBR_LOGICAL_DATA_SIG UINT32 Size; //!< Size of Data in bytes UINT32 LogicalIndex; //!< Index within the partition UINT8 Data[]; //!< Start of actual data }PbrPartitionLogicalDataItem; /**partition table, which describes location of each partition**/ typedef struct _PbrPartitionTable { PbrPartitionTableEntry Partitions[MAX_PARTITIONS]; }PbrPartitionTable; /**main pbr header that includes the partition table**/ typedef struct _PbrHeader { UINT32 Signature; //!< PBR_HEADER_SIG PbrPartitionTable PartitionTable; //!< Partition table, describes partition locations within a stitched img CHAR8 SwVersion[PBR_SW_VERSION_MAX]; //!< SW/Driver version used to record data CHAR8 OsVersion[PBR_OS_VERSION_MAX]; //!< Execution OS, i.e. UEFI/Linux/Windows CHAR8 OsName[PBR_OS_NAME_MAX]; //!< Execution OS name CHAR8 Description[PBR_FILE_DESCRIPTION_MAX]; //!< Highlevel description of pbr file }PbrHeader; /**tag data struct that is used within the tag partition**/ typedef struct _Tag { UINT32 Signature; //!< PBR_TAG_SIG UINT32 TagSignature; //!< type of tag UINT32 TagId; //!< Unique ID UINT32 TagSize; //!< Size of the tag that follows this data structure UINT32 PartitionInfoCnt; //!< The number of partition info items that follows this struct }Tag; typedef struct _TagPartitionInfo { UINT32 PartitionSignature; //!< Signature of the partition UINT32 PartitionCurrentOffset; //!< Playback or Recording offset of the partition }TagPartitionInfo; extern PbrContext gPbrContext; //!< extern global context #pragma pack(pop) #endif //_PBR_TYPES_H_ ipmctl-03.00.00.0485/DcpmPkg/common/PcdCommon.c000066400000000000000000000014201440615110200204500ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include /** Free dimm PCD info array @param[in, out] pDimmPcdInfo Pointer to output array of PCDs @param[in, out] DimmPcdInfoCount Number of items in Dimm PCD Info **/ VOID FreeDimmPcdInfoArray( IN OUT DIMM_PCD_INFO *pDimmPcdInfo, IN OUT UINT32 DimmPcdInfoCount ) { UINT32 Index = 0; NVDIMM_ENTRY(); if (pDimmPcdInfo == NULL) { goto Finish; } for (Index = 0; Index < DimmPcdInfoCount; Index++) { FREE_POOL_SAFE(pDimmPcdInfo[Index].pConfHeader); FreeLsaSafe(&pDimmPcdInfo[Index].pLabelStorageArea); } FREE_POOL_SAFE(pDimmPcdInfo); Finish: NVDIMM_EXIT(); } ipmctl-03.00.00.0485/DcpmPkg/common/PcdCommon.h000066400000000000000000000435171440615110200204720ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _PCD_COMMON_H_ #define _PCD_COMMON_H_ #include #include #if defined(_MSC_VER) #pragma warning( push ) #pragma warning( disable : 4200 ) #endif extern EFI_GUID gIntelDimmConfigVariableGuid; #define PCD_TARGET_ALL 0 #define PCD_TARGET_CONFIG 1 #define PCD_TARGET_NAMESPACES 2 #define NVDIMM_CONFIGURATION_HEADER_SIG SIGNATURE_32('D', 'M', 'H', 'D') #define NVDIMM_CURRENT_CONFIG_SIG SIGNATURE_32('C', 'C', 'U', 'R') #define NVDIMM_CONFIGURATION_INPUT_SIG SIGNATURE_32('C', 'I', 'N', '_') #define NVDIMM_CONFIGURATION_OUTPUT_SIG SIGNATURE_32('C', 'O', 'U', 'T') #define NVDIMM_CONFIGURATION_HEADER_LOWEST_COMPATIBLE_REVISION 1 #define NVDIMM_CONFIGURATION_HEADER_OEM_ID "INTEL " #define NVDIMM_CONFIGURATION_HEADER_OEM_ID_LEN 6 #define NVDIMM_CONFIGURATION_HEADER_OEM_TABLE_ID SIGNATURE_64('I', 'P', 'M', 'C', 'T', 'L', ' ', ' ') #define NVDIMM_CONFIGURATION_HEADER_OEM_REVISION 2 #define NVDIMM_CONFIGURATION_HEADER_CREATOR_ID SIGNATURE_32('I', 'N', 'T', 'L') #define NVDIMM_CONFIGURATION_HEADER_CREATOR_REVISION 0 #define NVDIMM_CONFIGURATION_TABLES_REVISION_1 1 #define NVDIMM_CONFIGURATION_TABLES_REVISION_2 2 #define NVDIMM_CONFIGURATION_TABLES_MAJOR_REVISION_1 1 #define NVDIMM_CONFIGURATION_TABLES_MAJOR_REVISION_3 3 #define NVDIMM_CONFIGURATION_TABLES_MINOR_REVISION_1 1 #define NVDIMM_CONFIGURATION_TABLES_MINOR_REVISION_2 2 #define NVDIMM_CONFIGURATION_TABLES_MINOR_REVISION_3 3 #define NVDIMM_CONFIGURATION_TABLES_REVISION_DEFAULT NVDIMM_CONFIGURATION_TABLES_REVISION_2 #define IS_NVDIMM_CONFIGURATION_HEADER_REV_INVALID(table) ((table->Header.Revision.AsUint8 != NVDIMM_CONFIGURATION_TABLES_REVISION_1) && (table->Header.Revision.AsUint8 != NVDIMM_CONFIGURATION_TABLES_REVISION_2) && \ ((table->Header.Revision.Split.Major != NVDIMM_CONFIGURATION_TABLES_MAJOR_REVISION_1) || (table->Header.Revision.Split.Minor != NVDIMM_CONFIGURATION_TABLES_MINOR_REVISION_1)) && \ ((table->Header.Revision.Split.Major != NVDIMM_CONFIGURATION_TABLES_MAJOR_REVISION_1) || (table->Header.Revision.Split.Minor != NVDIMM_CONFIGURATION_TABLES_MINOR_REVISION_2)) && \ ((table->Header.Revision.Split.Major != NVDIMM_CONFIGURATION_TABLES_MAJOR_REVISION_1) || (table->Header.Revision.Split.Minor != NVDIMM_CONFIGURATION_TABLES_MINOR_REVISION_3)) && \ ((table->Header.Revision.Split.Major != NVDIMM_CONFIGURATION_TABLES_MAJOR_REVISION_3) || (table->Header.Revision.Split.Minor != NVDIMM_CONFIGURATION_TABLES_MINOR_REVISION_1)) && \ ((table->Header.Revision.Split.Major != NVDIMM_CONFIGURATION_TABLES_MAJOR_REVISION_3) || (table->Header.Revision.Split.Minor != NVDIMM_CONFIGURATION_TABLES_MINOR_REVISION_2))) #define LSA_NAMESPACE_INDEX_SIG_L SIGNATURE_64('N', 'A', 'M', 'E', 'S', 'P', 'A', 'C') #define LSA_NAMESPACE_INDEX_SIG_H SIGNATURE_64('E', '_', 'I', 'N', 'D', 'E', 'X', '\0') #define LSA_NAMESPACE_INDEX_SIG (LSA_NAMESPACE_INDEX_SIG_L | LSA_NAMESPACE_INDEX_SIG_H << 64) /** PCAT table types **/ #define PCAT_TYPE_PARTITION_SIZE_CHANGE_TABLE 4 #define PCAT_TYPE_INTERLEAVE_INFORMATION_TABLE 5 #define NVDIMM_MEMORY_PERSISTENT_TYPE 2 #define NVDIMM_MEMORY_PERSISTENT_CACHED_TYPE 3 #define PART_NUMBER_SIZE 20 #define NVDIMM_CONFIG_HEADER_CURRENT_SIZE_OFFSET 36 #define NVDIMM_CONFIG_HEADER_CURRENT_START_OFFSET 40 #define NVDIMM_CONFIG_HEADER_INPUT_SIZE_OFFSET 44 #define NVDIMM_CONFIG_HEADER_INPUT_START_OFFSET 48 #define NVDIMM_CONFIG_HEADER_OUTPUT_SIZE_OFFSET 52 #define NVDIMM_CONFIG_HEADER_OUTPUT_START_OFFSET 56 #define NVDIMM_TABLE_HEADER_REVISION 1 #define NVDIMM_TABLE_HEADER_OEM_TABLE_ID 0 #define NVDIMM_TABLE_HEADER_OEM_REVISION 2 #define NVDIMM_TABLE_HEADER_CREATOR_REVISION 1 #define NVDIMM_CONF_INPUT_PART_SIZE_CHANGE_STATUS 0 #define PCD_PARTITION_SIZE 0x20000 #define PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE 0x10000 #define PCD_OEM_PARTITION_ID 1 #define PCD_LSA_PARTITION_ID 2 #define CONFIG_OUTPUT_STATUS_UNKNOWN 0 #define CONFIG_OUTPUT_STATUS_SUCCESS 1 #define CONFIG_OUTPUT_STATUS_ERROR 2 #define CONFIG_OUTPUT_STATUS_NM_FM_RATIO_UNSUPPORTED 6 #define CONFIG_OUTPUT_STATUS_CPU_MAX_MEMORY_LIMIT_VIOLATION 7 #define CONFIG_OUTPUT_STATUS_POPULATION_ISSUE 8 #define PARTITION_SIZE_CHANGE_STATUS_UNDEFINED 0 #define PARTITION_SIZE_CHANGE_STATUS_SUCCESS 1 #define PARTITION_SIZE_CHANGE_STATUS_RESERVED 2 #define PARTITION_SIZE_CHANGE_STATUS_DIMM_MISSING 3 #define PARTITION_SIZE_CHANGE_STATUS_ISET_MISSING 4 #define PARTITION_SIZE_CHANGE_STATUS_EXCEED_SIZE 5 #define PARTITION_SIZE_CHANGE_STATUS_FW_ERROR 6 #define PARTITION_SIZE_CHANGE_STATUS_EXCEED_DRAM_DECODERS 7 #define PARTITION_SIZE_CHANGE_STATUS_UNSUPPORTED_ALIGNMENT 8 #define INTERLEAVE_INFO_STATUS_UNDEFINED 0 #define INTERLEAVE_INFO_STATUS_SUCCESS 1 #define INTERLEAVE_INFO_STATUS_NOT_PROCESSED 2 #define INTERLEAVE_INFO_STATUS_DIMM_MISSING 3 #define INTERLEAVE_INFO_STATUS_ISET_MISSING 4 #define INTERLEAVE_INFO_STATUS_EXCEED_DRAM_DECODERS 5 #define INTERLEAVE_INFO_STATUS_EXCEED_MAX_SPA_SPACE 6 #define INTERLEAVE_INFO_STATUS_PARTITIONING_FAILED 8 #define INTERLEAVE_INFO_STATUS_CIN_MISSING 9 #define INTERLEAVE_INFO_STATUS_CHANNEL_NOT_MATCH 10 #define INTERLEAVE_INFO_STATUS_UNSUPPORTED_ALIGNMENT 11 #define INTERLEAVE_INFO_STATUS_REQUEST_UNSUPPORTED 12 #define MAX_PCD_TABLE_STATUS_LENGTH 12 /** Tables below are stored on DIMMs **/ #pragma pack(push) #pragma pack(1) typedef struct { /** HEADER **/ TABLE_HEADER Header; //!< Signature for this table: 'DMHD' /** BODY **/ UINT32 CurrentConfDataSize; //!< Size of data area allocated to the current configuration information in bytes UINT32 CurrentConfStartOffset; //!< Starting location of current configuration. Valid if the size is non-zero UINT32 ConfInputDataSize; //!< Size of data area allocated to the configuration request area in bytes UINT32 ConfInputStartOffset; //!< Starting location of configuration request data. Valid if the size is non-zero UINT32 ConfOutputDataSize; //!< Size of data area allocated to the configuration response area in bytes UINT32 ConfOutputStartOffset; //!< Starting location of configuration response data. Valid if the size is non-zero } NVDIMM_CONFIGURATION_HEADER; typedef struct { /** HEADER **/ TABLE_HEADER Header; //!< Signature for this table: 'CCUR' /** BODY **/ /** Status codes: 00 - Undefined 01 - DIMM is configured successfully 02 - Reserved 03 - All the modules in the interleave set not found. Volatile memory is mapped to the SPA if possible 04 - Persistent Memory not mapped due to matching Interleave set not found. Volatile memory is mapped to the SPA if possible 05 - DIMM added to the system or moved within the system or DIMM is not yet configured Volatile memory is mapped to the SPA if possible. Current configuration present in the DIMM is not modified (Reserved) 06 - New configuration input structures have errors, old configuration used. Refer to the config output structures for additional errors 07 - New configuration input structures have errors. Volatile memory is mapped to the SPA if possible Refer to the config output structures for addition errors 08 - Configuration Input Checksum not valid 09 - Configuration Input data Revision is not supported 10 - PCD Partition #0 Current Configuration Checksum not valid 11 - Module is not mapped to SPA due to a health issue or configuration change 12 - Module is not mapped due to a population issue 13 - Module volatile memory is not mapped since NM:FM ratio is not supported 14 - Module is not mapped due to a violation of the CPU maximum memory limit 15 - Module persistent memory mapped, but volatile memory if any is not mapped due to a population issue **/ UINT16 ConfigStatus; UINT8 Reserved[2]; UINT64 VolatileMemSizeIntoSpa; //!< Total amount of 2LM size from the module mapped into the SPA UINT64 PersistentMemSizeIntoSpa; //!< Total amount of PM size from the module mapped into the SPA /** A list of PCAT table structures **/ VOID *pPcatTables[0]; } NVDIMM_CURRENT_CONFIG; typedef enum { Undefined = 0, Success = 1, Reserved = 2, NotAllDimmsInInterleaveFound = 3, MatchinInterleaveNotFound = 4, PartitionSizeExceedsSizeInput = 5, FwError = 6, InsufficientDramDecoders = 7, PersistentMemoryNotAligned = 8 } PartitionSizeChangeStatusCode; typedef struct { /** HEADER **/ PCAT_TABLE_HEADER Header; //!< Type: 4 /** BODY **/ /** Valid only on the config output structure. Byte0: 0 - Undefined 1 - Success 2 - Reserved 3 - All the modules in the interleave set not found 4 - Matching Interleave set not found (Matching modules found) 5 - Total partition size defined in the interleave set exceeds the partition size input 6 - Module FW returned error 7 - Insufficient silicon resources available to map all the modules in the interleave set. Repartition not performed. 8 - Persistent memory partition size is not aligned to the Interleave Alignment Size specified in the Interface Capability Information Table Byte1: DIMM FW Error Response Code. Only valid boot time records, runtime validation will not invoke DIMM FW to get the data **/ UINT32 PartitionSizeChangeStatus; /** Size of persistent memory partition in bytes. This size must be aligned to "Interleave Alignment Size" specified in the "Interface Capability Information" table. **/ UINT64 PmPartitionSize; } NVDIMM_PARTITION_SIZE_CHANGE; /** Interleave change or current interleave information **/ typedef struct { /** HEADER **/ PCAT_TABLE_HEADER Header; //!< Type: 5 /** BODY **/ UINT16 InterleaveSetIndex; //!< Logical index number, it should same for all the DIMMs in the interleave set /** Total number of modules participating in this interleave set. DIMM Info structure at the end of this structure is repeated for each module in the interleave set **/ UINT8 NumOfDimmsInInterleaveSet; /** 1 - Reserved 2 - App Direct PM (Persistent) 3 - Reserved **/ UINT8 InterleaveMemoryType; /** Byte0 - Channel Interleave Size Byte1 - iMC Interleave Size One of the supported values from PCAT Memory Interleave Capability Info Table **/ UINT8 InterleaveFormatChannel; UINT8 InterleaveFormatImc; UINT8 Reserved1[3]; /** Config Input: Reserved zero Config Output: 0 - Information not processed 1 - Successfully interleaved the request 2 - Information not processed due to an error 3 - Unable to find matching modules in the interleave set 4 - Matching modules found, but interleave information does not match 5 - Insufficient silicon resources available to map all the modules in the interleave set. This interleave set may not be mapped to system address space 6 - Memory mapping failed due to unavailable system address space 7 - Reserved 8 - Partitioning request failed 9 - Matching modules found, but CIN missing in a module in the interleave set 10 - Channel interleave does not match between the MCs being interleaved 11 - Partition Offset or Size is not a multiple of Interleave Alignment Size in Memory Interleave Capability Information sub-table of PCAT 12 - Request unsupported **/ UINT8 InterleaveChangeStatus; UINT8 Reserved3[10]; VOID *pIdentificationInfoList[0]; //!< DIMM interleave set information } NVDIMM_INTERLEAVE_INFORMATION3; /** Interleave change or current interleave information **/ typedef struct { /** HEADER **/ PCAT_TABLE_HEADER Header; //!< Type: 5 /** BODY **/ UINT16 InterleaveSetIndex; //!< Logical index number, it should same for all the DIMMs in the interleave set /** Total number of DIMMs participating in this interleave set. DIMM Info structure at the end of this structure is repeated for each DIMM in the interleave set **/ UINT8 NumOfDimmsInInterleaveSet; /** 1 - 2LM (Volatile) 2 - App Direct PM (Persistent) 3 - Reserved **/ UINT8 InterleaveMemoryType; /** Interleave format to be used for this interleave set. One of value from Interleave Format List table **/ UINT8 InterleaveFormatChannel; UINT8 InterleaveFormatImc; UINT16 InterleaveFormatWays; UINT8 Reserved1; /** Config Input: Reserved zero Config Output: 0 - Information not processed 1 - Successfully interleaved the request 2 - Unable to find matching DIMMs in the interleave set 3 - Matching DIMMs found, but interleave information does not match 4 - Insufficient number of DRAM Decoders available to map all the DIMMs in the interleave set. This interleave set may not be mapped to system address space 5 - Memory mapping failed due to unavailable system address space **/ UINT8 InterleaveChangeStatus; UINT8 MemorySpare; UINT8 Reserved[9]; VOID *pIdentificationInfoList[0]; //!< DIMM interleave set information } NVDIMM_INTERLEAVE_INFORMATION; typedef struct _DIMM_UNIQUE_IDENTIFIER { UINT16 ManufacturerId; //!< Indicates the manufacturer of the module UINT8 ManufacturingLocation; //!< Unique manufacturing location of the module UINT16 ManufacturingDate; //!< Indicates manufacturing year and week UINT32 SerialNumber; //!< Unique serial number for the module } DIMM_UNIQUE_IDENTIFIER; typedef union { UINT64 AsUint64; struct { UINT32 SocketId :8; UINT32 DieId :8; UINT32 MemControllerId :8; UINT32 ChannelId :8; UINT32 SlotId :8; UINT32 Reserved :24; }Split; } DIMM_LOCATION; typedef struct { DIMM_UNIQUE_IDENTIFIER DimmIdentification; /** Byte0 - Socket ID Byte1 - Die ID Byte2 - iMC ID Byte3 - Channel ID Byte4 - Slot ID Byte[5-7] - Reserved **/ DIMM_LOCATION DimmLocation; UINT8 Reserved[15]; /** Logical offset from the base of the partition type in bytes. DPA = FW returned DPA for partition + offset **/ UINT64 PartitionOffset; UINT64 PmPartitionSize; //!< Size in bytes contributed by this module for this interleave set } NVDIMM_IDENTIFICATION_INFORMATION3; typedef struct { union { struct { UINT16 DimmManufacturerId; //!< Manufacturer ID string from DIMM UINT32 DimmSerialNumber; //!< Serial number string from DIMM CHAR8 DimmPartNumber[PART_NUMBER_SIZE]; //!< Dimm part number UINT8 Reserved[6]; } Version1; struct { DIMM_UNIQUE_IDENTIFIER Uid; UINT8 Reserved[23]; } Version2; }DimmIdentification; /** Logical offset from the base of the partition type in bytes. DPA = FW returned DPA for partition + offset **/ UINT64 PartitionOffset; UINT64 PmPartitionSize; //!< Size in bytes contributed by this DIMM for this interleave set } NVDIMM_IDENTIFICATION_INFORMATION; typedef struct { /** HEADER **/ TABLE_HEADER Header; //!< Signature for this table: 'CIN_' /** BODY **/ /** Request sequence number. The BIOS copies this sequence number to the response structure once the BIOS completes processing the request input **/ UINT32 SequenceNumber; UINT8 Reserved[8]; /** A list of PCAT table structures **/ VOID *pPcatTables[0]; } NVDIMM_PLATFORM_CONFIG_INPUT; typedef struct { /** HEADER **/ TABLE_HEADER Header; //!< Signature for this table: 'COUT' /** BODY **/ /** Copy of the configure input structure sequence number to indicate that the BIOS has completed processing input structure **/ UINT32 SequenceNumber; /** BIOS response after config input processing 0 - Undefined 1 - Config Change applied successfully 2 - Boot time processing complete, errors found. Refer to individual records for error details 6 - Goal not applied because the NM:FM ratio is not supported 7 - Goal not applied due to a violation of the CPU maximum memory limit 8 - Goal not applied due to a population issue **/ UINT8 ValidationStatus; UINT8 Reserved[7]; /** A list of PCAT table structures **/ VOID *pPcatTables[0]; } NVDIMM_PLATFORM_CONFIG_OUTPUT; #pragma pack(pop) typedef struct _DIMM_PCD_INFO { UINT32 DimmId; CHAR16 DimmUid[MAX_DIMM_UID_LENGTH]; NVDIMM_CONFIGURATION_HEADER *pConfHeader; LABEL_STORAGE_AREA *pLabelStorageArea; } DIMM_PCD_INFO; /** Free dimm PCD info array @param[in, out] pDimmPcdInfo Pointer to output array of PCDs @param[in, out] DimmPcdInfoCount Number of items in Dimm PCD Info **/ VOID FreeDimmPcdInfoArray( IN OUT DIMM_PCD_INFO *pDimmPcdInfo, IN OUT UINT32 DimmPcdInfoCount ); #define GET_NVDIMM_CURRENT_CONFIG(NvdimmConfHeader) ((NVDIMM_CURRENT_CONFIG *) \ GET_VOID_PTR_OFFSET(NvdimmConfHeader, NvdimmConfHeader->CurrentConfStartOffset)) #define GET_NVDIMM_PLATFORM_CONFIG_INPUT(NvdimmConfHeader) ((NVDIMM_PLATFORM_CONFIG_INPUT *) \ GET_VOID_PTR_OFFSET(NvdimmConfHeader, NvdimmConfHeader->ConfInputStartOffset)) #define GET_NVDIMM_PLATFORM_CONFIG_OUTPUT(NvdimmConfHeader) ((NVDIMM_PLATFORM_CONFIG_OUTPUT *) \ GET_VOID_PTR_OFFSET(NvdimmConfHeader, NvdimmConfHeader->ConfOutputStartOffset)) #if defined(_MSC_VER) #pragma warning( pop ) #endif #endif ipmctl-03.00.00.0485/DcpmPkg/common/Printer.c000066400000000000000000001460561440615110200202330ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "Printer.h" #include #include #include #include #define EXPAND_STR_MAX 1024 #define XML_FILE_BEGIN L"\n" #define ESX_XML_FILE_BEGIN L"" #define ESX_XML_FILE_END L"\n" #define ESX_XML_STRUCT_LIST_TAG_BEGIN L"\n" #define ESX_XML_SIMPLE_STR_TAG_END L"]]>" #define ESX_XML_SIMPLE_STR_TAG_BEGIN L"\n" #define ESX_XML_LIST_STRING_BEGIN L"" #define NVM_XML_DATA_SET_TAG_START L"<" FORMAT_STR L">\n" #define NVM_XML_KEY_VAL_TAG L"<" FORMAT_STR L">" FORMAT_STR L"\n" #define NVM_XML_DATA_SET_TAG_END L"\n" #define NVM_XML_WHITESPACE_IDENT L" " #define NVM_XML_RESULT_BEGIN L"\n\n" #define MVM_XML_RESULT_END L"\n\n" #define TEXT_TABLE_DEFAULT_DELIM L'|' #define TEXT_NEW_LINE L"\n" #define TEXT_TABLE_HEADER_SEP L"=" #define TEXT_LIST_WHITESPACE_IDENT L" " #define TEXT_LIST_IGNORE_LIST_DELIM L';' #define TEXT_LIST_HEADER L"---" #define TEXT_LIST_KEY_VAL_DELIM L"=" #define CHAR_NULL_TERM L'\0' #define CHAR_PATH_DELIM L'/' #define CHAR_WHITE_SPACE L' ' #define CELL_EXTRA_CHARS 2 //1 for leading whitespace and 1 for terminating pipe BOOLEAN gDisplayNulls = FALSE; UINT32 gNullValuesEncounteredForDisplay = 0; CHAR16* gNullValueToDisplay = L"NULL"; typedef enum { PRINT_TEXT, PRINT_BASIC_XML, PRINT_XML }PRINT_MODE; typedef struct _PRV_TABLE_INFO { PRINTER_TABLE_ATTRIB *AllTableAttribs; PRINTER_TABLE_ATTRIB *ModifiedTableAttribs; CHAR16 *CurrentPath; CHAR16 *PrinterNode; }PRV_TABLE_INFO; /** Helper for iterating through buffered object list **/ #define BUFFERED_OBJECT_LIST_FOR_EACH_SAFE(Entry, NextEntry, ListHead) \ for(Entry = (ListHead)->ForwardLink, NextEntry = Entry->ForwardLink; \ Entry != (ListHead); \ Entry = NextEntry, NextEntry = Entry->ForwardLink \ ) #define PRINT_ONLY_DATA_SETS(pPrintCtx) \ (XML == pPrintCtx->FormatType && 1 == pPrintCtx->BufferedDataSetCnt && EFI_SUCCESS == pPrintCtx->BufferedObjectLastError) \ /** Helper for finding the list attribute associated with a particular data set @param[in] LevelType: Name of the data set that represents a particular level in the hierarchy @param[in] Attribs: Contains a LIST_LEVEL_ATTRIB for each level in the hierarchy @retval LIST_LEVEL_ATTRIB* that represents the LevelType @retval NULL if not found **/ static LIST_LEVEL_ATTRIB *TextListFindAttrib(IN CHAR16 *LevelType, IN PRINTER_LIST_ATTRIB *Attribs) { UINT32 Index; if (!LevelType || !Attribs) { return NULL; } for (Index = 0; Index < MAX_LIST_LEVELS; Index ++) { if (Attribs->LevelAttribs[Index].LevelType && (0 == StrCmp(LevelType, Attribs->LevelAttribs[Index].LevelType))) { return &Attribs->LevelAttribs[Index]; } } return NULL; } /** Expands a string by replacing $(key) identifiers with the associated value if the key/value pair exist in the data set. Example: ---Dimm=$(DimmId)--- Expands to: ---Dimm=0x0001--- @param[in] DataSetCtx: Data set contains key/val pairs @param[in] Str: May contain $(key) identifiers that need to be replaced with corresponding values @retval CHAR16* Expanded version of the input Str. **/ static CHAR16 *TextListExpandStr(IN DATA_SET_CONTEXT *DataSetCtx, IN const CHAR16 *Str) { CHAR16 *Val = NULL; CHAR16 *ExpandedStr = AllocateZeroPool(EXPAND_STR_MAX); const CHAR16 *OriginalStrTmpBegin = Str; const CHAR16 *OriginalStrTmpEnd = OriginalStrTmpBegin; CHAR16 *ExpandedStrTmp = ExpandedStr; CHAR16 *MacroKeyName = NULL; UINTN AppendSize = 0; UINTN FreeSize = EXPAND_STR_MAX - sizeof(CHAR16); //minus one for NULL terminator if (NULL == ExpandedStr || NULL == Str) { goto Finish; } while (*OriginalStrTmpBegin != CHAR_NULL_TERM) { AppendSize = 0; //Found beginning of $(key) identifier if (OriginalStrTmpBegin[0] == L'$' && OriginalStrTmpBegin[1] == L'(') { //Find end of $(key) identifier do { ++OriginalStrTmpEnd; } //Find end of $(key) identifier or end of str, which ever comes first while(*OriginalStrTmpEnd != CHAR_NULL_TERM && *OriginalStrTmpEnd != L')'); //OriginalStrTmpBegin points to beginning of $(key) identifier, OriginalStrTmpEnd points to the end //allocate enough memory to copy the string that sits between pointers //plus 1 char worth of mem for NULL term to make it a string MacroKeyName = AllocateZeroPool(((UINTN)OriginalStrTmpEnd - (UINTN)OriginalStrTmpBegin) + sizeof(CHAR16)); if (NULL == MacroKeyName) { NVDIMM_CRIT("AllocateZeroPool returned NULL\n"); goto Finish; } CopyMem(MacroKeyName, OriginalStrTmpBegin, ((UINTN)OriginalStrTmpEnd - (UINTN)OriginalStrTmpBegin)); //Get the value associated with the key identifier //TempKey[2] - skips past "$(" prefix GetKeyValueWideStr(DataSetCtx, (CHAR16*)&MacroKeyName[2], &Val, NULL); if (NULL != Val) { //don't copy NULL terminator AppendSize = StrSize(Val) - sizeof(CHAR16); if(0 > (INTN)(FreeSize - AppendSize)) goto Finish; //don't use str copy APIs as Val may contain format specifiers CopyMem(ExpandedStrTmp, Val, AppendSize); ExpandedStrTmp += StrLen(Val); } else { //don't copy NULL terminator AppendSize = StrSize(MacroKeyName) - sizeof(CHAR16); if (0 > (INTN)(FreeSize - AppendSize)) { goto Finish; } //don't use str copy APIs as Val may contain format specifiers CopyMem(ExpandedStrTmp, MacroKeyName, AppendSize); ExpandedStrTmp += StrLen(MacroKeyName); } FREE_POOL_SAFE(MacroKeyName); //Advance OriginalStrTmpEnd past the last part of the $(key) identifier if(*OriginalStrTmpEnd == L')') { ++OriginalStrTmpEnd; } //Reset OriginalStrTmpBegin OriginalStrTmpBegin = OriginalStrTmpEnd; } else { AppendSize = sizeof(*OriginalStrTmpBegin); if (0 > (INTN)(FreeSize - AppendSize)) { goto Finish; } *ExpandedStrTmp = *OriginalStrTmpBegin; ++ExpandedStrTmp; ++OriginalStrTmpBegin; ++OriginalStrTmpEnd; } FreeSize -= AppendSize; } Finish: FREE_POOL_SAFE(MacroKeyName); return ExpandedStr; } /* * Helper to create xml indentions when printing based on depth of CurPath */ static UINT32 NvmXmlGetNvmXmlIdent(CHAR16 *CurPath) { UINT32 NumSpaces = 0; while (*CurPath != CHAR_NULL_TERM) { if (*CurPath++ == CHAR_PATH_DELIM) { ++NumSpaces; } } return NumSpaces; } /** Helper to create text list indentions when printing based on depth of CurPath **/ static INT32 TextListGetListLevel(IN CHAR16 *CurPath) { INT32 GroupLevel = 0; if (!CurPath) { return GroupLevel; } while (*CurPath != CHAR_NULL_TERM) { if (*CurPath++ == CHAR_PATH_DELIM) { ++GroupLevel; } } return --GroupLevel; } /** Helper to create text list indentions when printing based on depth of CurPath @param[in] CurPath: Represents a hierarchical path of data sets in the form of /sensorlist/dimm/sensor @retval UINT32 Whitespace count **/ static UINT32 TextListGetPrintIdentCount(IN CHAR16 *CurPath) { return 0; //currently no default indentation } /** Helper to create text list indentions when printing based on a count value @param[in] Count: Affects how many whitespaces to print **/ static VOID TextListPrintIdent(IN UINT32 Count) { while (Count--) { Print(TEXT_LIST_WHITESPACE_IDENT); } } /** Helper TextListCb to determine if a key should be displayed or not **/ static BOOLEAN TextListIgnoreKey(IN CHAR16 *IgnoreKeyList[], IN UINT32 IgnoreListSz, IN CHAR16 *Key) { UINT32 Index; if(!IgnoreKeyList || !Key) { return FALSE; } for (Index = 0; Index < IgnoreListSz; ++Index) { if (0 == StrCmp((const CHAR16*)IgnoreKeyList[Index], (const CHAR16*)Key)) { return TRUE; } } return FALSE; } /** The callback routine for displaying a hierarchical list @param[in] DataSetCtx: Represents a data set that contains key/val pairs @param[in] CurPath: Represents a hierarchical path of data sets in the form of /sensorlist/dimm/sensor @param[in] UserData: Represents a PRINTER_LIST_ATTRIB struct @param[in] ParentUserData: Not used in this context @retval VOID * Always returns NULL **/ static VOID * TextListCb(IN DATA_SET_CONTEXT *DataSetCtx, IN CHAR16 *CurPath, IN VOID *UserData, IN VOID *ParentUserData) { KEY_VAL_INFO *KvInfo = NULL; CHAR16 *Val = NULL; PRINTER_LIST_ATTRIB *Attribs = (PRINTER_LIST_ATTRIB *)UserData; LIST_LEVEL_ATTRIB *LevelAttribs = NULL; CHAR16 *Header = NULL; CHAR16 **Toks = NULL; UINT32 NumToks = 0; BOOLEAN ListHeaderPrinted = FALSE; UINT32 IdentCount = TextListGetPrintIdentCount(CurPath); //Example:CurPath == /sensorlist/dimm //Group level is base 0, there value will be 1 INT32 ListGroupLevel = TextListGetListLevel(CurPath); //if CMD is overriding default text list attributes if(Attribs) { //extract the list attribute associated with the current data set node LevelAttribs = TextListFindAttrib(GetDataSetName(DataSetCtx), Attribs); //if a header attribute is specified if (LevelAttribs && LevelAttribs->LevelHeader) { //overridden header may contain key/val "macros" in the form of L"---DimmId=$(DimmId)---" //where $(DimmId) is the name of a key within a particular data set. //TextListExpandStr will replace macros with associated key value. if (NULL != (Header = TextListExpandStr(DataSetCtx, LevelAttribs->LevelHeader))) { //print whitespace indentation based on depth of data set node TextListPrintIdent(IdentCount); Print(FORMAT_STR_NL, Header); //free the expanded header FreePool(Header); ListHeaderPrinted = TRUE; } } } //CMD can specify keys to ignore (don't display) if(LevelAttribs && LevelAttribs->IgnoreKeyValList) { Toks = StrSplit((CHAR16*)LevelAttribs->IgnoreKeyValList, TEXT_LIST_IGNORE_LIST_DELIM, &NumToks); } //Iterate through current data set's key/values and display them while (NULL != (KvInfo = GetNextKey(DataSetCtx, KvInfo))) { //If key is not on ignore list, print it if (!TextListIgnoreKey(Toks, NumToks, KvInfo->Key)) { //retrieve the value in wide string form GetKeyValueWideStr(DataSetCtx, KvInfo->Key, &Val, NULL); //print a whitespace indentation TextListPrintIdent(IdentCount); //by default 1). the data set's group header is the first key/val pair found //2). the first group in the list does not have a header if (!ListHeaderPrinted && ListGroupLevel > 0) { Print(TEXT_LIST_HEADER); } else { TextListPrintIdent(IdentCount); } //default is to display key/val pair as Key=Value unless overridden if (LevelAttribs && LevelAttribs->LevelKeyValFormatStr && StrLen(LevelAttribs->LevelKeyValFormatStr) > 0) { Print(LevelAttribs->LevelKeyValFormatStr, KvInfo->Key, Val); } else { Print(FORMAT_STR TEXT_LIST_KEY_VAL_DELIM FORMAT_STR, KvInfo->Key, Val); } //by default only group level 2 and greater will include the --- if (!ListHeaderPrinted && ListGroupLevel > 1) { Print(TEXT_LIST_HEADER); } Print(TEXT_NEW_LINE); ListHeaderPrinted = TRUE; } } FreeStringArray(Toks, NumToks); return NULL; } /** Main entry point for displaying a hierarchical list @param[in] DataSetCtx: Represents a data set that contains key/val pairs @param[in] PrinterListAttribs: User specified attributes that defines how a list should be printed **/ static VOID PrintTextList(IN DATA_SET_CONTEXT *DataSetCtx, IN PRINTER_LIST_ATTRIB *PrinterListAttribs) { RecurseDataSet(DataSetCtx, TextListCb, NULL, (VOID*)PrinterListAttribs, TRUE); } /** The node that represents the last cell in a row is responsible for printing the entire row. This is a helper to determine if a particular node is the "printer node". @param[in] DataSetCtx: Represents a data set that contains key/val pairs @param[in] CurPath: Represents a hierarchical path of data sets in the form of /sensorlist/dimm/sensor @param[in] PrvTableInfo: Contains information related to a table @retval BOOLEAN TRUE if CurPath points to the designated "printer node" else FALSE **/ static BOOLEAN TextTableIsPrinterNode(IN DATA_SET_CONTEXT *DataSetCtx, IN CHAR16 *CurPath, IN PRV_TABLE_INFO *PrvTableInfo) { CHAR16 *TmpStr = CatSPrint(CurPath,L"."); BOOLEAN IsPrinter = FALSE; if (NULL == TmpStr) { NVDIMM_CRIT("CatSPrint returned NULL\n"); goto Finish; } if(NULL == PrvTableInfo) { goto Finish; } IsPrinter = (0 == StrnCmp(TmpStr, PrvTableInfo->PrinterNode, StrLen(TmpStr))); Finish: FREE_POOL_SAFE(TmpStr); return IsPrinter; } /** This routine takes a Path and returns a pointer to the keyname. @param[in] Path: In the form of /sensorlist/dimm/sensor.keyname. @retval CHAR16 * Pointer to keyname else NULL terminator if keyname not found. **/ static const CHAR16 *TextTableFindKeyInPath(IN const CHAR16 *Path) { const CHAR16 *Key = Path; if(!Key) { return Path; } while (*Key != CHAR_NULL_TERM && *Key != L'.') { ++Key; } if(*Key == L'.') { ++Key; } return Key; } /** Helper to determine how many columns to print in a table. @param[in] Attribs: list of table attributes, each representing a column @retval UINTN Number of columns to print **/ static UINTN NumTableColumns(IN PRINTER_TABLE_ATTRIB *Attribs) { UINTN Index = 0; if(!Attribs) { return Index; } for (Index = 0; Index < MAX_TABLE_COLUMNS; ++Index) { if(!Attribs->ColumnAttribs[Index].ColumnHeader) { break; } } return Index; } /** Callback routine for printing out text tables. @param[in] DataSetCtx: current data set node @param[in] CurPath: path to current data set node in the form of: /sensorlist/dimm/sensor @param[in] UserData: pointer to PRV_TABLE_INFO (defines user defined table/column attributes) @param[in] ParentUserData: a string that represents the current table row (note, last node in branch is responsible for printing the row) @retval CHAR16* that represents the table row. @retval NULL on error **/ static VOID * TextTableCb(IN DATA_SET_CONTEXT *DataSetCtx, IN CHAR16 *CurPath, IN VOID *UserData, IN VOID *ParentUserData) { PRV_TABLE_INFO *PrvTableInfo = (PRV_TABLE_INFO *)UserData; PRINTER_TABLE_ATTRIB *Attribs; CHAR16 *TempCurPath = CatSPrint(CurPath, L"."); UINT32 ColumnIndex = 0; UINT32 CellValueIndex = 0; CHAR16 *KeyVal; CHAR16 *CurRowText = CatSPrint(ParentUserData, L""); CHAR16 *EndOfRowText; UINTN RowSizeInBytes; UINTN NumColumns = 0; UINTN KeyValSize = 0; UINTN MaxCellChars = 0; CHAR16 *TempRowText = NULL; BOOLEAN LastNodeInBranch = FALSE; CHAR16 *EmptyCell = L"X"; if(NULL == UserData || NULL == CurRowText || NULL == TempCurPath) { goto Finish; } LastNodeInBranch = TextTableIsPrinterNode(DataSetCtx, CurPath, PrvTableInfo); Attribs = (PRINTER_TABLE_ATTRIB *)PrvTableInfo->AllTableAttribs; NumColumns = NumTableColumns(Attribs); //Loop through all column attributes defined by the CLI cmd handler for (ColumnIndex = 0; ColumnIndex < NumColumns; ++ColumnIndex) { //column attributes defines which key/value pairs to display in each column. //This is done by specifying a path to a key in the form of: /sensorlist/dimm/sensor.keyname. //TempCurPath represents the current node in path form with a '.' at the end, i.e. /sensorlist/dimm/sensor. if (0 == StrnCmp(TempCurPath, Attribs->ColumnAttribs[ColumnIndex].ColumnDataSetPath, StrLen(TempCurPath))) { //Found a path in column attributes that points to our current node, try to retrieve the //associated value. GetKeyValueWideStr(DataSetCtx, TextTableFindKeyInPath(Attribs->ColumnAttribs[ColumnIndex].ColumnDataSetPath), &KeyVal, NULL); if(NULL == KeyVal) { KeyVal = EmptyCell; } //if we are at the last column, let the value wrap if (LastNodeInBranch && ColumnIndex == (NumColumns-1)) { MaxCellChars = StrLen(KeyVal); RowSizeInBytes = StrSize(CurRowText) + ((MaxCellChars + CELL_EXTRA_CHARS) * sizeof(CHAR16)); } //CurRowText contains cells in a row already processed, now add more memory for the next cell in the row //if we are not at the last row, restrict the size of the cell as specified in table attributes else { MaxCellChars = Attribs->ColumnAttribs[ColumnIndex].ColumnMaxStrLen; RowSizeInBytes = StrSize(CurRowText) + ((MaxCellChars + CELL_EXTRA_CHARS) * sizeof(CHAR16)); } if (NULL == (CurRowText = ReallocatePool(StrSize(CurRowText), RowSizeInBytes, CurRowText))) { goto Finish; } //Make EndOfRowText point to end of the already processed row EndOfRowText = &CurRowText[StrLen(CurRowText)]; //Always start cell with a space *EndOfRowText++ = CHAR_WHITE_SPACE; //Number of chars in the new column value KeyValSize = StrLen(KeyVal); //Add data to the row string (new column value) for (CellValueIndex = 0; CellValueIndex < MaxCellChars; ++CellValueIndex) { //fill the column cell with the key value if (CellValueIndex < KeyValSize) { EndOfRowText[CellValueIndex] = KeyVal[CellValueIndex]; } //pad the rest of the cell with whitespaces (up to the specified column max width) else { EndOfRowText[CellValueIndex] = CHAR_WHITE_SPACE; } } if((ColumnIndex + 1) != NumColumns){ EndOfRowText[CellValueIndex] = TEXT_TABLE_DEFAULT_DELIM; ++CellValueIndex; } EndOfRowText[CellValueIndex] = CHAR_NULL_TERM; } } //determine if the current node is the "printer node" (last node in branch) if (LastNodeInBranch) { TempRowText = CurRowText; while (*TempRowText != CHAR_NULL_TERM) { //print individual characters as string may contain format specifiers. Print(FORMAT_CHAR, *TempRowText++); } Print(TEXT_NEW_LINE); } Finish: FREE_POOL_SAFE(TempCurPath); //return a string that represents the current row. return CurRowText; } /** Callback routine for determining max column widths of a text table @param[in] DataSetCtx: current data set node @param[in] CurPath: path to current data set node in the form of: /sensorlist/dimm/sensor @param[in] UserData: pointer to PRV_TABLE_INFO (defines user defined table/column attributes) @param[in] ParentUserData: a string that represents the current table row (note, last node in branch is responsible for printing the row) @retval CHAR16* that represents the table row. @retval NULL on error **/ static VOID * CalculateTextTableDimensionCb(IN DATA_SET_CONTEXT *DataSetCtx, IN CHAR16 *CurPath, IN VOID *UserData, IN VOID *ParentUserData) { PRV_TABLE_INFO *PrvTableInfo = (PRV_TABLE_INFO *)UserData; PRINTER_TABLE_ATTRIB *Attribs; PRINTER_TABLE_ATTRIB *ModifiedAttribs; CHAR16 *TempCurPath = CatSPrint(CurPath, L"."); UINT32 ColumnIndex = 0; UINTN NumColumns = 0; UINTN MaxCellChars = 0; CHAR16 *EmptyCell = L"X"; CHAR16 *KeyVal; if (NULL == UserData || NULL == TempCurPath) { goto Finish; } Attribs = (PRINTER_TABLE_ATTRIB *)PrvTableInfo->AllTableAttribs; ModifiedAttribs = (PRINTER_TABLE_ATTRIB *)PrvTableInfo->ModifiedTableAttribs; NumColumns = NumTableColumns(Attribs); //Loop through all column attributes defined by the CLI cmd handler for (ColumnIndex = 0; ColumnIndex < NumColumns; ++ColumnIndex) { //column attributes defines which key/value pairs to display in each column. //This is done by specifying a path to a key in the form of: /sensorlist/dimm/sensor.keyname. //TempCurPath represents the current node in path form with a '.' at the end, i.e. /sensorlist/dimm/sensor. if (0 == StrnCmp(TempCurPath, Attribs->ColumnAttribs[ColumnIndex].ColumnDataSetPath, StrLen(TempCurPath))) { //Found a path in column attributes that points to our current node, try to retrieve the //associated value. GetKeyValueWideStr(DataSetCtx, TextTableFindKeyInPath(Attribs->ColumnAttribs[ColumnIndex].ColumnDataSetPath), &KeyVal, NULL); if (NULL == KeyVal) { KeyVal = EmptyCell; } MaxCellChars = StrLen(KeyVal) + 1; if (MaxCellChars < Attribs->ColumnAttribs[ColumnIndex].ColumnMaxStrLen && MaxCellChars > ModifiedAttribs->ColumnAttribs[ColumnIndex].ColumnMaxStrLen) { ModifiedAttribs->ColumnAttribs[ColumnIndex].ColumnMaxStrLen = (UINT32)MaxCellChars; } //reset to original max width specified by the attributes table else if (MaxCellChars >= Attribs->ColumnAttribs[ColumnIndex].ColumnMaxStrLen) { ModifiedAttribs->ColumnAttribs[ColumnIndex].ColumnMaxStrLen = Attribs->ColumnAttribs[ColumnIndex].ColumnMaxStrLen; } } } Finish: FREE_POOL_SAFE(TempCurPath); return NULL; } /** The node that represents the last cell in a row is responsible for printing the entire row. This is a helper to create a path in the form of /sensorlist/dimm/sensor that points to a particular "printer node". **/ static CHAR16 * TextTableGetPrinterNodePath(PRINTER_TABLE_ATTRIB * TableAttribs) { UINT32 Index = 0; CHAR16 *PrinterNodePath = NULL; UINT32 DeepestLevel = 0; UINT32 TempLevelCount = 0; CHAR16 *TempPath; for (Index = 0; Index < MAX_TABLE_COLUMNS; ++Index) { if(!TableAttribs->ColumnAttribs[Index].ColumnDataSetPath) { break; } TempPath = (CHAR16*)TableAttribs->ColumnAttribs[Index].ColumnDataSetPath; TempLevelCount = 0; while (*TempPath != CHAR_NULL_TERM) { if (*TempPath == CHAR_PATH_DELIM) { ++TempLevelCount; } ++TempPath; } if (TempLevelCount > DeepestLevel) { PrinterNodePath = (CHAR16*)TableAttribs->ColumnAttribs[Index].ColumnDataSetPath; } } return PrinterNodePath; } /* * Main entry point for displaying a hierarchical data set as a table */ VOID PrintDataSetAsTextTable(DATA_SET_CONTEXT *DataSetCtx, PRINTER_TABLE_ATTRIB * Attribs) { UINT32 Index = 0; UINT32 Index2 = 0; PRV_TABLE_INFO PrvTableInfo; CHAR16 *TableHeaderStart = NULL; CHAR16 *TableHeaderEnd = NULL; UINTN RowSizeInBytes = 0; UINTN RowSizeInChars = 0; UINTN NumColumns = NumTableColumns(Attribs); if (NULL == Attribs) { NVDIMM_CRIT("CMDs must specify a PRINTER_TABLE_ATTRIB when displaying text tables\n"); return; } TableHeaderStart = CatSPrint(NULL, L""); if (NULL == TableHeaderStart) { return; } //Loop generates a string that represents the table's header for (Index = 0; Index < NumColumns; ++Index) { //TableRowStart contains cells in a row already processed, now add more memory for the next cell in the row RowSizeInBytes = StrSize(TableHeaderStart) + ((Attribs->ColumnAttribs[Index].ColumnMaxStrLen + CELL_EXTRA_CHARS) * sizeof(CHAR16)); //+CELL_EXTRA_CHARS on ColumnWidth to accommodate whitespace and pipe if (NULL == (TableHeaderStart = ReallocatePool(StrSize(TableHeaderStart), RowSizeInBytes, TableHeaderStart))) { return; } //TableHeaderEnd points to end of the already processed row cells TableHeaderEnd = &TableHeaderStart[StrLen(TableHeaderStart)]; //Always start cell with a space *TableHeaderEnd++ = CHAR_WHITE_SPACE; for (Index2 = 0; Index2 < Attribs->ColumnAttribs[Index].ColumnMaxStrLen; ++Index2) { if (Index2 < StrLen(Attribs->ColumnAttribs[Index].ColumnHeader)) { TableHeaderEnd[Index2] = Attribs->ColumnAttribs[Index].ColumnHeader[Index2]; } else { TableHeaderEnd[Index2] = CHAR_WHITE_SPACE; } } if ((Index + 1) != NumColumns) { TableHeaderEnd[Index2] = TEXT_TABLE_DEFAULT_DELIM; ++Index2; } TableHeaderEnd[Index2] = CHAR_NULL_TERM; } //Print the header while (TableHeaderStart[RowSizeInChars] != CHAR_NULL_TERM) { Print(FORMAT_CHAR, TableHeaderStart[RowSizeInChars]); ++RowSizeInChars; } FreePool(TableHeaderStart); Print(TEXT_NEW_LINE); //Print the header/body separator for (Index = 0; Index < RowSizeInChars; ++Index) { Print(TEXT_TABLE_HEADER_SEP); } Print(TEXT_NEW_LINE); PrvTableInfo.AllTableAttribs = Attribs; PrvTableInfo.PrinterNode = TextTableGetPrinterNodePath(Attribs); //Print the body of the table RecurseDataSet(DataSetCtx, TextTableCb, NULL, (VOID*)&PrvTableInfo, TRUE); } /* * Determine column widths of a text table */ VOID CalculateTextTableDimensions(DATA_SET_CONTEXT *DataSetCtx, PRINTER_TABLE_ATTRIB * Attribs, PRINTER_TABLE_ATTRIB * ModifiedAttribs) { UINT32 Index = 0; PRV_TABLE_INFO PrvTableInfo; UINTN NumColumns = NumTableColumns(Attribs); UINTN ColumnHeaderStrLen = 0; if (NULL == Attribs || NULL == ModifiedAttribs) { NVDIMM_CRIT("CMDs must specify a PRINTER_TABLE_ATTRIB when displaying text tables\n"); return; } //calculate max column header widths for (Index = 0; Index < NumColumns; ++Index) { ColumnHeaderStrLen = StrLen(Attribs->ColumnAttribs[Index].ColumnHeader) + 1; if (ColumnHeaderStrLen < Attribs->ColumnAttribs[Index].ColumnMaxStrLen) { ModifiedAttribs->ColumnAttribs[Index].ColumnMaxStrLen = (UINT32)ColumnHeaderStrLen; } } PrvTableInfo.AllTableAttribs = Attribs; PrvTableInfo.ModifiedTableAttribs = ModifiedAttribs; PrvTableInfo.PrinterNode = TextTableGetPrinterNodePath(Attribs); //calculate max column widths based on table content RecurseDataSet(DataSetCtx, CalculateTextTableDimensionCb, NULL, (VOID*)&PrvTableInfo, TRUE); } /* * Callback routine for printing out NVM XML. * -Start by printing indentation whitespace based on depth of node in tree. * -Print beginning tag: * -ForEach KeyValue Pair: * - Print indentation whitespace * - Print KeyVal * Note, closing tag for happens in the NvmlXmlChildrenDoneCb callback routine. */ static VOID * NvmlXmlCb(DATA_SET_CONTEXT *DataSetCtx, CHAR16 *CurPath, VOID *UserData, VOID *ParentUserData) { KEY_VAL_INFO *KvInfo = NULL; CHAR16 *Val = NULL; CHAR16 *Key = NULL; UINT32 Index = 0; UINT32 Ident = 0; Ident = NvmXmlGetNvmXmlIdent(CurPath); for(Index = 0; Index < Ident; ++Index) { Print(NVM_XML_WHITESPACE_IDENT); } Print(NVM_XML_DATA_SET_TAG_START, GetDataSetName(DataSetCtx)); while (NULL != (KvInfo = GetNextKey(DataSetCtx, KvInfo))) { GetKeyValueWideStr(DataSetCtx, KvInfo->Key, &Val, NULL); for (Index = 0; Index < Ident+1; ++Index) { Print(NVM_XML_WHITESPACE_IDENT); } Key = CatSPrint(NULL, KvInfo->Key); RemoveAllWhiteSpace(Key); TrimString(Val); Print(NVM_XML_KEY_VAL_TAG, Key, Val, Key); FREE_POOL_SAFE(Key); } return NULL; } /* * Children done callback routine for printing out NVM XML. * -Start by printing indentation whitespace based on depth of node in tree. * -Print closing tag: */ static VOID * NvmlXmlChildrenDoneCb(DATA_SET_CONTEXT *DataSetCtx, CHAR16 *CurPath, VOID *UserData) { UINT32 Index = 0; UINT32 Ident = 0; Ident = NvmXmlGetNvmXmlIdent(CurPath); for (Index = 0; Index < Ident; ++Index) { Print(NVM_XML_WHITESPACE_IDENT); } Print(NVM_XML_DATA_SET_TAG_END, GetDataSetName(DataSetCtx)); return NULL; } /* * Main entry point for displaying a hierarchical data set as a table */ static VOID PrintNvmXml(DATA_SET_CONTEXT *DataSetCtx) { RecurseDataSet(DataSetCtx, NvmlXmlCb, NvmlXmlChildrenDoneCb, NULL, TRUE); } /* * Callback routine for printing out ESX XML. * -Start by printing indentation whitespace based on depth of node in tree. * -Print beginning tag: * -ForEach KeyValue Pair: * - "Value" * -Print closing tag: */ static VOID * EsxXmlCb(DATA_SET_CONTEXT *DataSetCtx, CHAR16 *CurPath, VOID *UserData, VOID *ParentUserData) { KEY_VAL_INFO *KvInfo = NULL; CHAR16 *Val = NULL; CHAR16 *Key = NULL; if (0 == GetKeyCount(DataSetCtx)) { return NULL; } Print(L"\n", GetDataSetName(DataSetCtx)); while (NULL != (KvInfo = GetNextKey(DataSetCtx, KvInfo))) { GetKeyValueWideStr(DataSetCtx, KvInfo->Key, &Val, NULL); Key = CatSPrint(NULL, KvInfo->Key); RemoveAllWhiteSpace(Key); TrimString(Val); Print(L" " FORMAT_STR L"\n", Key, Val); FREE_POOL_SAFE(Key); } Print(L"\n"); return NULL; } /* * Main entry point for displaying a hierarchical data set as ESX XML. */ static VOID PrintEsxXml(DATA_SET_CONTEXT *DataSetCtx) { Print(ESX_XML_FILE_BEGIN); Print(ESX_XML_STRUCT_LIST_TAG_BEGIN); RecurseDataSet(DataSetCtx, EsxXmlCb, NULL, NULL, TRUE); Print(ESX_XML_LIST_TAG_END); Print(ESX_XML_FILE_END); } /* * Callback routine for printing out ESX XML (keyval pair struct list). * -ForEach KeyValue Pair: * - * - MemoryCapacity0 B * - */ static VOID * EsxKeyValXmlCb(DATA_SET_CONTEXT *DataSetCtx, CHAR16 *CurPath, VOID *UserData, VOID *ParentUserData) { KEY_VAL_INFO *KvInfo = NULL; CHAR16 *Val = NULL; CHAR16 *Key = NULL; if (0 == GetKeyCount(DataSetCtx)) { return NULL; } while (NULL != (KvInfo = GetNextKey(DataSetCtx, KvInfo))) { GetKeyValueWideStr(DataSetCtx, KvInfo->Key, &Val, NULL); Key = CatSPrint(NULL, KvInfo->Key); RemoveAllWhiteSpace(Key); TrimString(Val); Print(L"\n"); Print(L" " FORMAT_STR L"" FORMAT_STR L"\n", Key, Val); Print(L"\n"); FREE_POOL_SAFE(Key); } return NULL; } /* * Main entry point for displaying a hierarchical data set as ESX XML (keyval pair struct list). */ static VOID PrintEsxKeyValXml(DATA_SET_CONTEXT *DataSetCtx) { Print(ESX_XML_FILE_BEGIN); Print(ESX_XML_STRUCT_LIST_TAG_BEGIN); RecurseDataSet(DataSetCtx, EsxKeyValXmlCb, NULL, NULL, TRUE); Print(ESX_XML_LIST_TAG_END); Print(ESX_XML_FILE_END); } /* * Main entry point for displaying a hierarchical data set as text. * Function determines if format is a list or table. */ static VOID PrintAsText(DATA_SET_CONTEXT *DataSetCtx, PRINT_CONTEXT *PrintCtx) { PRINTER_DATA_SET_ATTRIBS *Attribs = (PRINTER_DATA_SET_ATTRIBS *)GetDataSetUserData(DataSetCtx); PRINTER_LIST_ATTRIB *ListAttribs = NULL; PRINTER_TABLE_ATTRIB *TableAttribs = NULL; PRINTER_TABLE_ATTRIB *ModifiedTableAttribs = (PRINTER_TABLE_ATTRIB *)AllocateZeroPool(sizeof(PRINTER_TABLE_ATTRIB)); if (NULL == ModifiedTableAttribs) { NVDIMM_CRIT("AllocateZeroPool returned NULL\n"); return; } if (PrintCtx->FormatTypeFlags.Flags.List) { if (Attribs) { ListAttribs = Attribs->pListAttribs; } PrintTextList(DataSetCtx, ListAttribs); } else if (PrintCtx->FormatTypeFlags.Flags.Table) { if (Attribs) { TableAttribs = Attribs->pTableAttribs; if(TableAttribs) { CopyMem_S(ModifiedTableAttribs, sizeof(PRINTER_TABLE_ATTRIB), TableAttribs, sizeof(PRINTER_TABLE_ATTRIB)); } } else { NVDIMM_CRIT("CMDs must specify a PRINTER_TABLE_ATTRIB when displaying text tables\n"); goto Finish; } CalculateTextTableDimensions(DataSetCtx, TableAttribs, ModifiedTableAttribs); PrintDataSetAsTextTable(DataSetCtx, ModifiedTableAttribs); } Finish: FREE_POOL_SAFE(ModifiedTableAttribs); } /* * Main entry point for displaying a hierarchical data set as XML. * Function determines which XML format to display. */ static VOID PrintAsXml(DATA_SET_CONTEXT *DataSetCtx, PRINT_CONTEXT *PrintCtx) { DATA_SET_CONTEXT *SquashedDataSet; SquashedDataSet = SquashDataSet(DataSetCtx); if (PrintCtx->FormatTypeFlags.Flags.EsxCustom) { PrintEsxXml(SquashedDataSet); } else if (PrintCtx->FormatTypeFlags.Flags.EsxKeyVal) { PrintEsxKeyValXml(SquashedDataSet); } else { PrintNvmXml(SquashedDataSet); } //SquashDataSet will return original Data set if it has no children //If it doesn't squash, don't free it. if(SquashedDataSet != DataSetCtx) { FreeDataSet(SquashedDataSet); } } /* * Print to stdout with each line starting with ERROR */ static VOID PrintTextAsEsxError(CHAR16 *Msg) { CHAR16 *BeginStr = Msg; CHAR16 *SearchResult = NULL; CHAR16 *tmpStr = NULL; UINT32 Index; if (NULL == Msg) { return; } while (NULL != (SearchResult = StrStr(BeginStr, FORMAT_NL))) { tmpStr = (CHAR16 *)AllocateZeroPool((1 + SearchResult - BeginStr) * sizeof(Msg[0])); if (NULL != tmpStr) { // replace StrnCpy since it is not available in all environments for (Index = 0; Index < (SearchResult - BeginStr); ++Index) { tmpStr[Index] = BeginStr[Index]; } Print(ESX_ERROR_LINE_HEADER); Print(FORMAT_STR FORMAT_NL, tmpStr); FREE_POOL_SAFE(tmpStr); BeginStr = SearchResult + 1; } else { // Cannot copy out substr so just print it all Print(ESX_ERROR_LINE_HEADER); Print(FORMAT_STR FORMAT_NL, BeginStr); BeginStr += StrLen(BeginStr); } } if (0 < StrLen(BeginStr)) { Print(ESX_ERROR_LINE_HEADER); Print(FORMAT_STR FORMAT_NL, BeginStr); } } /* * Print to stdout and ensure newline */ static VOID PrintTextWithNewLine(CHAR16 *Msg) { UINTN MsgLen = 0; if (NULL == Msg) { return; } MsgLen = StrLen(Msg); Print(FORMAT_STR, Msg); if (MsgLen && Msg[MsgLen - 1] != L'\n') { Print(L"\n"); } } /* * Display beginning of XML error */ static VOID PrintXmlStartErrorTag(PRINT_CONTEXT *PrintCtx, EFI_STATUS CmdExitCode) { if (!PrintCtx->FormatTypeFlags.Flags.EsxCustom && !PrintCtx->FormatTypeFlags.Flags.EsxKeyVal) { #ifdef OS_BUILD CmdExitCode = UefiToOsReturnCode(CmdExitCode); #endif Print(L"\n", CmdExitCode); } } /* * Display ending of XML error */ static VOID PrintXmlEndErrorTag(PRINT_CONTEXT *PrintCtx, EFI_STATUS CmdExitCode) { if (!PrintCtx->FormatTypeFlags.Flags.EsxCustom && !PrintCtx->FormatTypeFlags.Flags.EsxKeyVal) { Print(L"\n"); } } /* * Display beginning of XML success msg */ static VOID PrintXmlStartSuccessTag(PRINT_CONTEXT *PrintCtx, EFI_STATUS CmdExitCode) { if (PrintCtx->FormatTypeFlags.Flags.EsxCustom || PrintCtx->FormatTypeFlags.Flags.EsxKeyVal) { Print(ESX_XML_FILE_BEGIN); Print(ESX_XML_SIMPLE_STR_TAG_BEGIN); } else { Print(NVM_XML_RESULT_BEGIN); } } /* * Display ending of XML success msg */ static VOID PrintXmlEndSuccessTag(PRINT_CONTEXT *PrintCtx, EFI_STATUS CmdExitCode) { if (PrintCtx->FormatTypeFlags.Flags.EsxCustom || PrintCtx->FormatTypeFlags.Flags.EsxKeyVal) { Print(ESX_XML_SIMPLE_STR_TAG_END); Print(ESX_XML_FILE_END); } else { Print(MVM_XML_RESULT_END); } } /* * Helper that creates a message out of a COMMAND_STATUS object. */ static EFI_STATUS CreateCmdStatusMsg(CHAR16 **ppMsg, CHAR16 *pStatusMessage, CHAR16 *pStatusPreposition, BOOLEAN DoNotPrintGeneralStatusSuccessCode, COMMAND_STATUS *pCommandStatus) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT8 DimmIdentifier = 0; BOOLEAN ObjectIdNumberPreferred = FALSE; ReturnCode = GetDimmIdentifierPreference(&DimmIdentifier); if (EFI_ERROR(ReturnCode)) { goto Finish; } ObjectIdNumberPreferred = DimmIdentifier == DISPLAY_DIMM_ID_HANDLE; ReturnCode = CreateCommandStatusString(gNvmDimmCliHiiHandle, pStatusMessage, pStatusPreposition, pCommandStatus, ObjectIdNumberPreferred, DoNotPrintGeneralStatusSuccessCode, ppMsg); Finish: return ReturnCode; } /* * Create a printer context */ EFI_STATUS PrinterCreateCtx( OUT PRINT_CONTEXT **ppPrintCtx ) { EFI_STATUS ReturnCode = EFI_SUCCESS; if (NULL == ppPrintCtx) { return EFI_INVALID_PARAMETER; } if (NULL == (*ppPrintCtx = (PRINT_CONTEXT*)AllocateZeroPool(sizeof(PRINT_CONTEXT)))) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } InitializeListHead(&((*ppPrintCtx)->BufferedObjectList)); InitializeListHead(&((*ppPrintCtx)->DataSetLookup)); InitializeListHead(&((*ppPrintCtx)->DataSetRootLookup)); (*ppPrintCtx)->DoNotPrintGeneralStatusSuccessCode = FALSE; Finish: return ReturnCode; } /* * Helper to free all items in the "lookup list" */ static VOID CleanDataSetLookupItems( IN PRINT_CONTEXT *pPrintCtx ) { LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; DATA_SET_LOOKUP_ITEM *DataSetLookupItem = NULL; if (NULL == pPrintCtx) { return; } BUFFERED_OBJECT_LIST_FOR_EACH_SAFE(Entry, NextEntry, &pPrintCtx->DataSetLookup) { DataSetLookupItem = BASE_CR(Entry, DATA_SET_LOOKUP_ITEM, Link); RemoveEntryList(&DataSetLookupItem->Link); FREE_POOL_SAFE(DataSetLookupItem->DsPath); FREE_POOL_SAFE(DataSetLookupItem); } BUFFERED_OBJECT_LIST_FOR_EACH_SAFE(Entry, NextEntry, &pPrintCtx->DataSetRootLookup) { DataSetLookupItem = BASE_CR(Entry, DATA_SET_LOOKUP_ITEM, Link); RemoveEntryList(&DataSetLookupItem->Link); FREE_DATASET_RECURSIVE_SAFE(DataSetLookupItem->pDataSet); //recursive free, no need to free children data sets. FREE_POOL_SAFE(DataSetLookupItem->DsPath); FREE_POOL_SAFE(DataSetLookupItem); } } /* * Destroys a printer context */ EFI_STATUS PrinterDestroyCtx( IN PRINT_CONTEXT *pPrintCtx ) { LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; BUFFERED_PRINTER_OBJECT *BufferedObject = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; if (NULL == pPrintCtx) { return EFI_INVALID_PARAMETER; } BUFFERED_OBJECT_LIST_FOR_EACH_SAFE(Entry, NextEntry, &pPrintCtx->BufferedObjectList) { BufferedObject = BASE_CR(Entry, BUFFERED_PRINTER_OBJECT, Link); RemoveEntryList(&BufferedObject->Link); if (BUFF_STR_TYPE == BufferedObject->Type) { BUFFERED_STR *pTempBs = (BUFFERED_STR *)BufferedObject->Obj; FREE_POOL_SAFE(pTempBs->pStr); } else if (BUFF_COMMAND_STATUS_TYPE == BufferedObject->Type) { BUFFERED_COMMAND_STATUS *pTempCs = (BUFFERED_COMMAND_STATUS *)BufferedObject->Obj; FreeCommandStatus(&pTempCs->pCommandStatus); FREE_POOL_SAFE(pTempCs->pStatusMessage); FREE_POOL_SAFE(pTempCs->pStatusPreposition); } FREE_POOL_SAFE(BufferedObject->Obj); FREE_POOL_SAFE(BufferedObject); } CleanDataSetLookupItems(pPrintCtx); FREE_POOL_SAFE(pPrintCtx); return ReturnCode; } /* * Helper to create a string object destined for the "set buffer" */ static EFI_STATUS CreateBufferedStrObj(BUFFERED_PRINTER_OBJECT **ppBufferedObj, EFI_STATUS Status, CHAR16 *Str) { BUFFERED_STR *pTempBuffStr; if (NULL == ppBufferedObj) { return EFI_INVALID_PARAMETER; } pTempBuffStr = (BUFFERED_STR*)AllocateZeroPool(sizeof(BUFFERED_STR)); *ppBufferedObj = (BUFFERED_PRINTER_OBJECT*)AllocateZeroPool(sizeof(BUFFERED_PRINTER_OBJECT)); if (*ppBufferedObj == NULL || pTempBuffStr == NULL) { FREE_POOL_SAFE(*ppBufferedObj); FREE_POOL_SAFE(pTempBuffStr); return EFI_OUT_OF_RESOURCES; } (*ppBufferedObj)->Type = BUFF_STR_TYPE; (*ppBufferedObj)->Obj = (VOID*)pTempBuffStr; (*ppBufferedObj)->Status = Status; pTempBuffStr->StrType = UNICODE_STR; pTempBuffStr->pStr = (VOID*)Str; return EFI_SUCCESS; } /* * Helper to create a dataset object destined for the "set buffer" */ static EFI_STATUS CreateBufferedDataSetObj(BUFFERED_PRINTER_OBJECT **ppBufferedObj, EFI_STATUS Status, DATA_SET_CONTEXT *pDataSetCtx) { BUFFERED_DATA_SET *pTempBuffStr; if (NULL == ppBufferedObj) { return EFI_INVALID_PARAMETER; } pTempBuffStr = (BUFFERED_DATA_SET*)AllocateZeroPool(sizeof(BUFFERED_DATA_SET)); *ppBufferedObj = (BUFFERED_PRINTER_OBJECT*)AllocateZeroPool(sizeof(BUFFERED_PRINTER_OBJECT)); if (*ppBufferedObj == NULL || pTempBuffStr == NULL) { FREE_POOL_SAFE(*ppBufferedObj); FREE_POOL_SAFE(pTempBuffStr); return EFI_OUT_OF_RESOURCES; } (*ppBufferedObj)->Type = BUFF_DATA_SET_TYPE; (*ppBufferedObj)->Obj = (VOID*)pTempBuffStr; (*ppBufferedObj)->Status = Status; pTempBuffStr->pDataSet = pDataSetCtx; return EFI_SUCCESS; } /* * Helper to create a dataset object destined for the "lookup list" */ static EFI_STATUS CreateDataSetLookupItem(DATA_SET_LOOKUP_ITEM **ppDataSetLookupItem, CHAR16 *pPath, DATA_SET_CONTEXT *pDataSetCtx) { if (NULL == ppDataSetLookupItem) { return EFI_INVALID_PARAMETER; } *ppDataSetLookupItem = (DATA_SET_LOOKUP_ITEM*)AllocateZeroPool(sizeof(DATA_SET_LOOKUP_ITEM)); if (*ppDataSetLookupItem == NULL) { return EFI_OUT_OF_RESOURCES; } (*ppDataSetLookupItem)->pDataSet = pDataSetCtx; (*ppDataSetLookupItem)->DsPath = CatSPrint(NULL, FORMAT_STR, pPath); return EFI_SUCCESS; } /* * Handle string messages */ EFI_STATUS EFIAPI PrinterSetMsg( IN PRINT_CONTEXT *pPrintCtx, IN EFI_STATUS Status, IN CHAR16 *pMsg, ... ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CHAR16 *FullMsg = NULL; VA_LIST Marker; if (NULL == pMsg) { NVDIMM_ERR("Invalid input parameter\n"); goto Finish; } VA_START(Marker, pMsg); FullMsg = CatVSPrint(NULL, pMsg, Marker); VA_END(Marker); //here for backwards compatibility if (NULL == pPrintCtx || (!pPrintCtx->FormatTypeFlags.Flags.Buffered && XML != pPrintCtx->FormatType)) { PrintTextWithNewLine(FullMsg); FREE_POOL_SAFE(FullMsg); return EFI_SUCCESS; } if (pPrintCtx->FormatTypeFlags.Flags.Buffered) { BUFFERED_PRINTER_OBJECT *pBufferedObj = NULL; if (EFI_SUCCESS != (ReturnCode = CreateBufferedStrObj(&pBufferedObj, Status, FullMsg))) { goto Finish; } InsertTailList(&pPrintCtx->BufferedObjectList, &pBufferedObj->Link); if (EFI_ERROR(Status)) { pPrintCtx->BufferedObjectLastError = Status; } pPrintCtx->BufferedMsgCnt++; } else { //here to handle the case where printer is unbuffered and XML == pPrintCtx->FormatType FREE_POOL_SAFE(FullMsg); } ReturnCode = EFI_SUCCESS; Finish: return ReturnCode; } /* * Handle dataset objects */ EFI_STATUS PrinterSetData( IN PRINT_CONTEXT *pPrintCtx, IN EFI_STATUS Status, IN DATA_SET_CONTEXT *pDataSetCtx ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; if (NULL == pDataSetCtx && NULL == pPrintCtx) { NVDIMM_ERR("Invalid input parameter\n"); goto Finish; } if (pPrintCtx->FormatTypeFlags.Flags.Buffered) { BUFFERED_PRINTER_OBJECT *pBufferedObj = NULL; if (EFI_SUCCESS != (ReturnCode = CreateBufferedDataSetObj(&pBufferedObj, Status, pDataSetCtx))) { goto Finish; } InsertTailList(&pPrintCtx->BufferedObjectList, &pBufferedObj->Link); if (EFI_ERROR(Status)) { pPrintCtx->BufferedObjectLastError = Status; } } pPrintCtx->BufferedDataSetCnt++; ReturnCode = EFI_SUCCESS; Finish: return ReturnCode; } /* * Handle command status objects */ EFI_STATUS PrinterSetCommandStatus( IN PRINT_CONTEXT *pPrintCtx, OPTIONAL IN EFI_STATUS Status, IN CHAR16 *pStatusMessage, IN CHAR16 *pStatusPreposition, IN COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CHAR16 *FullMsg = NULL; BOOLEAN DoNotPrintGeneralStatusSuccessCode = FALSE; if (NULL == pCommandStatus) { NVDIMM_ERR("Invalid input parameter\n"); goto Finish; } if (pPrintCtx != NULL) { DoNotPrintGeneralStatusSuccessCode = pPrintCtx->DoNotPrintGeneralStatusSuccessCode; } if (EFI_SUCCESS != (ReturnCode = CreateCmdStatusMsg(&FullMsg, pStatusMessage, pStatusPreposition, DoNotPrintGeneralStatusSuccessCode, pCommandStatus))) { goto Finish; } if (EFI_SUCCESS != (ReturnCode = PrinterSetMsg(pPrintCtx, Status, FullMsg))) { goto Finish; } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(FullMsg); return ReturnCode; } /* * Process mode */ static PRINT_MODE PrintMode( IN PRINT_CONTEXT *pPrintCtx ) { if (NULL == pPrintCtx) { return PRINT_TEXT; } if (XML == pPrintCtx->FormatType && 1 == pPrintCtx->BufferedDataSetCnt && EFI_SUCCESS == pPrintCtx->BufferedObjectLastError) { return PRINT_XML; } else if (XML == pPrintCtx->FormatType) { return PRINT_BASIC_XML; } else return PRINT_TEXT; } /* * Process all objects in the "set buffer" */ EFI_STATUS PrinterProcessSetBuffer( IN PRINT_CONTEXT *pPrintCtx ) { LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; BUFFERED_PRINTER_OBJECT *BufferedObject; EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 *FullMsg = NULL; PRINT_MODE PrinterMode = PRINT_TEXT; BOOLEAN startXmlSuccessPrinted = FALSE; BOOLEAN startXmlErrorPrinted = FALSE; if (NULL == pPrintCtx) { NVDIMM_ERR("Invalid input parameter\n"); return EFI_INVALID_PARAMETER; } PrinterMode = PrintMode(pPrintCtx); //if XML mode print the appropriate start tag if (PRINT_XML == PrinterMode || PRINT_BASIC_XML == PrinterMode) { if ((pPrintCtx->BufferedDataSetCnt != 0) || (EFI_SUCCESS == pPrintCtx->BufferedObjectLastError) || (!pPrintCtx->FormatTypeFlags.Flags.EsxCustom && !pPrintCtx->FormatTypeFlags.Flags.EsxKeyVal)) { Print(XML_FILE_BEGIN); } if (PRINT_BASIC_XML == PrinterMode && EFI_SUCCESS == pPrintCtx->BufferedObjectLastError) { PrintXmlStartSuccessTag(pPrintCtx, pPrintCtx->BufferedObjectLastError); startXmlSuccessPrinted = TRUE; } else if (EFI_SUCCESS != pPrintCtx->BufferedObjectLastError) { PrintXmlStartErrorTag(pPrintCtx, pPrintCtx->BufferedObjectLastError); startXmlErrorPrinted = TRUE; } } //iterate through all items in the "set buffer". //all items found should be transformed to text and printed directly to stdout BUFFERED_OBJECT_LIST_FOR_EACH_SAFE(Entry, NextEntry, &pPrintCtx->BufferedObjectList) { BufferedObject = BASE_CR(Entry, BUFFERED_PRINTER_OBJECT, Link); RemoveEntryList(&BufferedObject->Link); if (BUFF_STR_TYPE == BufferedObject->Type) { BUFFERED_STR *pTempBs = (BUFFERED_STR *)BufferedObject->Obj; // check if needs to be printed as error for Esx if ((pPrintCtx->BufferedDataSetCnt == 0) && (EFI_SUCCESS != pPrintCtx->BufferedObjectLastError) && (pPrintCtx->FormatTypeFlags.Flags.EsxCustom || pPrintCtx->FormatTypeFlags.Flags.EsxKeyVal)) { PrintTextAsEsxError(pTempBs->pStr); } else { if (PRINT_XML != PrinterMode) { PrintTextWithNewLine(pTempBs->pStr); } } FREE_POOL_SAFE(pTempBs->pStr); pPrintCtx->BufferedMsgCnt--; } else if (BUFF_DATA_SET_TYPE == BufferedObject->Type) { BUFFERED_DATA_SET *pTempDs = (BUFFERED_DATA_SET *)BufferedObject->Obj; if (PRINT_XML == PrinterMode) { PrintAsXml(pTempDs->pDataSet, pPrintCtx); } else { PrintAsText(pTempDs->pDataSet, pPrintCtx); } pPrintCtx->BufferedDataSetCnt--; } else if (BUFF_COMMAND_STATUS_TYPE == BufferedObject->Type) { BUFFERED_COMMAND_STATUS *pTempCs = (BUFFERED_COMMAND_STATUS *)BufferedObject->Obj; CreateCmdStatusMsg(&FullMsg, pTempCs->pStatusMessage, pTempCs->pStatusPreposition, pPrintCtx->DoNotPrintGeneralStatusSuccessCode, pTempCs->pCommandStatus); if (PRINT_XML != PrinterMode) { PrintTextWithNewLine(FullMsg); } FreeCommandStatus(&pTempCs->pCommandStatus); FREE_POOL_SAFE(pTempCs->pStatusMessage); FREE_POOL_SAFE(pTempCs->pStatusPreposition); FREE_POOL_SAFE(FullMsg); pPrintCtx->BufferedCmdStatusCnt--; } FREE_POOL_SAFE(BufferedObject->Obj); FREE_POOL_SAFE(BufferedObject); } if (TRUE == startXmlErrorPrinted) { PrintXmlEndErrorTag(pPrintCtx, pPrintCtx->BufferedObjectLastError); } else if (TRUE == startXmlSuccessPrinted) { PrintXmlEndSuccessTag(pPrintCtx, pPrintCtx->BufferedObjectLastError); } CleanDataSetLookupItems(pPrintCtx); pPrintCtx->BufferedObjectLastError = EFI_SUCCESS; return ReturnCode; } /* * Helper that associates a table/list of attributes with a dataset */ EFI_STATUS SetDataSetPrinterAttribs( IN PRINT_CONTEXT *pPrintCtx, IN CHAR16 *pKeyPath, IN PRINTER_DATA_SET_ATTRIBS *pAttribs ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CHAR16 **DataSetToks = NULL; UINT32 NumDataSetToks = 0; LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; DATA_SET_LOOKUP_ITEM *DataSetLookupItem = NULL; if (NULL == pPrintCtx || NULL == pKeyPath) { goto Finish; } // split path, result toks are data set names if (NULL == (DataSetToks = StrSplit(pKeyPath, L'/', &NumDataSetToks))) { ReturnCode = EFI_NOT_FOUND; goto Finish; } else if (NumDataSetToks < 2) { ReturnCode = EFI_NOT_FOUND; goto Finish; } ReturnCode = EFI_NOT_FOUND; BUFFERED_OBJECT_LIST_FOR_EACH_SAFE(Entry, NextEntry, &pPrintCtx->DataSetRootLookup) { DataSetLookupItem = BASE_CR(Entry, DATA_SET_LOOKUP_ITEM, Link); if (0 == StrCmp(DataSetToks[1], DataSetLookupItem->DsPath)) { SetDataSetUserData(DataSetLookupItem->pDataSet, (VOID *)pAttribs); ReturnCode = EFI_SUCCESS; break; } } Finish: FreeStringArray(DataSetToks, NumDataSetToks); return ReturnCode; } /* * Helper to lookup a dataset described by a path */ EFI_STATUS LookupDataSet( IN PRINT_CONTEXT *pPrintCtx, IN CHAR16 *pKeyPath, OUT DATA_SET_CONTEXT **ppDataSet ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CHAR16 **DataSetToks = NULL; UINT32 NumDataSetToks = 0; LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; DATA_SET_LOOKUP_ITEM *DataSetLookupItem = NULL; DATA_SET_CONTEXT *Root = NULL; BUFFERED_OBJECT_LIST_FOR_EACH_SAFE(Entry, NextEntry, &pPrintCtx->DataSetLookup) { DataSetLookupItem = BASE_CR(Entry, DATA_SET_LOOKUP_ITEM, Link); if (0 == StrCmp(pKeyPath, DataSetLookupItem->DsPath)) { *ppDataSet = DataSetLookupItem->pDataSet; return EFI_SUCCESS; } } //split path, result toks are data set names if (NULL == (DataSetToks = StrSplit(pKeyPath, L'/', &NumDataSetToks))) { ReturnCode = EFI_NOT_FOUND; goto Finish; } else if (NumDataSetToks < 2) { ReturnCode = EFI_NOT_FOUND; goto Finish; } BUFFERED_OBJECT_LIST_FOR_EACH_SAFE(Entry, NextEntry, &pPrintCtx->DataSetRootLookup) { DataSetLookupItem = BASE_CR(Entry, DATA_SET_LOOKUP_ITEM, Link); if (0 == StrCmp(DataSetToks[1], DataSetLookupItem->DsPath)) { Root = DataSetLookupItem->pDataSet; break; } } if(NULL == Root) { if(NULL == (Root = CreateDataSet(NULL, DataSetToks[1], NULL))) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } DataSetLookupItem = NULL; if (EFI_SUCCESS == (ReturnCode = CreateDataSetLookupItem(&DataSetLookupItem, DataSetToks[1], Root))) { InsertTailList(&pPrintCtx->DataSetRootLookup, &DataSetLookupItem->Link); } PRINTER_SET_DATA(pPrintCtx, EFI_SUCCESS, Root); } *ppDataSet = GetDataSet(Root, pKeyPath); DataSetLookupItem = NULL; if (EFI_SUCCESS == (ReturnCode = CreateDataSetLookupItem(&DataSetLookupItem, pKeyPath, *ppDataSet))) { InsertTailList(&pPrintCtx->DataSetLookup, &DataSetLookupItem->Link); } ReturnCode = EFI_SUCCESS; Finish: FreeStringArray(DataSetToks, NumDataSetToks); return ReturnCode; } /* * Helper that builds a path to a dataset node */ CHAR16 * EFIAPI BuildPath(CHAR16 *Format, ...) { CHAR16 *NewPath = NULL; VA_LIST Marker; if (NULL == Format) { return NULL; } VA_START(Marker, Format); NewPath = CatVSPrint(NULL, Format, Marker); VA_END(Marker); return NewPath; } ipmctl-03.00.00.0485/DcpmPkg/common/Printer.h000066400000000000000000000375351440615110200202410ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _PRINTER_H_ #define _PRINTER_H_ #include #include #include #include #define MAX_HEADER_NAME_SZ 100 #define MAX_LIST_LEVELS 10 #define MAX_TABLE_COLUMNS 15 #define SHOW_LIST_IDENT L" " #define DEFAULT_MAX_STR_WIDTH 30 #define ID_MAX_STR_WIDTH 10 #define DIMM_MAX_STR_WIDTH 22 #define CAPACITY_MAX_STR_WIDTH 25 #define HEALTH_MAX_STR_WIDTH 19 //worst-case - "Noncritical failure" #define HEALTH_SHORT_MAX_STR_WIDTH 17 #define SECURITY_MAX_STR_WIDTH 31 //worst-case - "Unlocked, Exceeded, MP Exceeded" #define FW_VERSION_MAX_STR_WIDTH 13 #define ACTIVE_FW_VERSION_MAX_STR_WIDTH 16 #define STAGED_FW_VERSION_MAX_STR_WIDTH 16 #define SENSOR_TYPE_MAX_STR_WIDTH 30 #define SENSOR_VALUE_MAX_STR_WIDTH 20 #define SENSOR_STATE_MAX_STR_WIDTH 15 #define SOCKET_MAX_STR_WIDTH 9 #define MAPPED_MEMORY_LIMIT_MAX_STR_WIDTH 23 //worst-case - "18446744073709551615 B" or "17592186044415.999 MiB" (UINT64 max value) #define TOTAL_MAPPED_MEMORY_MAX_STR_WIDTH 18 #define ISET_ID_MAX_STR_WIDTH 20 #define REGION_ID_MAX_STR_WIDTH 8 #define PMEM_TYPE_MAX_STR_WIDTH 24 #define FREE_CAPACITY_MAX_STR_WIDTH 25 #define MEMORY_SIZE_MAX_STR_WIDTH 18 #define MEMORY_TYPE_MAX_STR_WIDTH 30 #define DEVICE_LOCATOR_MAX_STR_WIDTH 15 #define DDR_MAX_STR_WIDTH 25 #define PMEM_MODULE_MAX_STR_WIDTH 25 #define TOTAL_STR_WIDTH 25 #define CAP_MB_RESTRICTION_MAX_STR_WIDTH 32 //worst-case - "Management, BIOS and SMBus only" #define ON 1 #define OFF 0 #define ESX_ERROR_LINE_HEADER L"ERROR: " // Helper to calculate the column width of a CHAR16 string literal #define TABLE_MIN_HEADER_LENGTH(Header) ((sizeof(Header) / sizeof(CHAR16))) extern BOOLEAN gDisplayNulls; extern UINT32 gNullValuesEncounteredForDisplay; extern CHAR16* gNullValueToDisplay; typedef enum { TEXT, XML }PRINT_FORMAT_TYPE; typedef enum { ASCII_STR, UNICODE_STR }STR_TYPE; typedef enum { BUFF_STR_TYPE, BUFF_DATA_SET_TYPE, BUFF_COMMAND_STATUS_TYPE }BUFFERED_TYPE; typedef struct _FLAGS { UINTN Buffered : 1; UINTN Table : 1; UINTN List : 1; UINTN EsxKeyVal : 1; UINTN EsxCustom : 1; UINTN Verbose : 1; }FLAGS; typedef union _PRINT_FORMAT_TYPE_FLAGS { FLAGS Flags; UINTN Val; }PRINT_FORMAT_TYPE_FLAGS; typedef struct _BUFFERED_PRINTER_OBJECT { LIST_ENTRY Link; BUFFERED_TYPE Type; EFI_STATUS Status; VOID *Obj; }BUFFERED_PRINTER_OBJECT; typedef struct _BUFFERED_STR { UINTN StrSize; //bytes STR_TYPE StrType; VOID *pStr; }BUFFERED_STR; typedef struct _BUFFERED_DATA_SET { DATA_SET_CONTEXT *pDataSet; }BUFFERED_DATA_SET; typedef struct _DATA_SET_LOOKUP_ITEM { LIST_ENTRY Link; CHAR16 *DsPath; DATA_SET_CONTEXT *pDataSet; }DATA_SET_LOOKUP_ITEM; typedef struct _BUFFERED_COMMAND_STATUS { CHAR16 *pStatusMessage; CHAR16 *pStatusPreposition; COMMAND_STATUS *pCommandStatus; }BUFFERED_COMMAND_STATUS; typedef struct _PRINT_CONTEXT { PRINT_FORMAT_TYPE FormatType; PRINT_FORMAT_TYPE_FLAGS FormatTypeFlags; VOID * UserData; LIST_ENTRY BufferedObjectList; EFI_STATUS BufferedObjectLastError; UINTN BufferedMsgCnt; UINTN BufferedCmdStatusCnt; UINTN BufferedDataSetCnt; LIST_ENTRY DataSetLookup; LIST_ENTRY DataSetRootLookup; BOOLEAN DoNotPrintGeneralStatusSuccessCode; }PRINT_CONTEXT; typedef struct _LIST_LEVEL_ATTRIB { CONST CHAR16 *LevelType; CONST CHAR16 *LevelHeader; CONST CHAR16 *LevelKeyValFormatStr; CONST CHAR16 *IgnoreKeyValList; }LIST_LEVEL_ATTRIB; typedef struct _PRINTER_LIST_ATTRIB { LIST_LEVEL_ATTRIB LevelAttribs[MAX_LIST_LEVELS]; }PRINTER_LIST_ATTRIB; typedef struct _TABLE_COLUMN_ATTRIB { CONST CHAR16 *ColumnHeader; UINT32 ColumnMaxStrLen; CONST CHAR16 *ColumnDataSetPath; }TABLE_COLUMN_ATTRIB; typedef struct _PRINTER_TABLE_ATTRIB { TABLE_COLUMN_ATTRIB ColumnAttribs[MAX_TABLE_COLUMNS]; }PRINTER_TABLE_ATTRIB; typedef struct _PRINTER_DATA_SET_ATTRIBS { PRINTER_LIST_ATTRIB *pListAttribs; PRINTER_TABLE_ATTRIB *pTableAttribs; }PRINTER_DATA_SET_ATTRIBS; /**Is running in ESX or ESXTable mode**/ #define PRINTER_ESX_FORMAT_ENABLED(Ctx) \ (NULL != Ctx && (Ctx->FormatTypeFlags.Flags.EsxKeyVal || Ctx->FormatTypeFlags.Flags.EsxCustom)) \ /**Display dataset as a table (default list)**/ #define PRINTER_ENABLE_TEXT_TABLE_FORMAT(Ctx) \ if(NULL != Ctx) { \ Ctx->FormatTypeFlags.Flags.Table = 1; \ Ctx->FormatTypeFlags.Flags.List = 0; \ } \ /**Display dataset as a list (default list)**/ #define PRINTER_ENABLE_LIST_TABLE_FORMAT(Ctx) \ if(NULL != Ctx) { \ Ctx->FormatTypeFlags.Flags.Table = 0; \ Ctx->FormatTypeFlags.Flags.List = 1; \ } \ /**Display dataset as ESX XML (-o esx)**/ #define PRINTER_ENABLE_ESX_XML_FORMAT(Ctx) \ if(NULL != Ctx) { \ Ctx->FormatTypeFlags.Flags.EsxKeyVal = 1; \ Ctx->FormatTypeFlags.Flags.EsxCustom = 0; \ } \ /**Display dataset as ESX XML (-o esxtable)**/ #define PRINTER_ENABLE_ESX_TABLE_XML_FORMAT(Ctx) \ if(NULL != Ctx) { \ Ctx->FormatTypeFlags.Flags.EsxKeyVal = 0; \ Ctx->FormatTypeFlags.Flags.EsxCustom = 1; \ } \ /**Set printer format attributes directly to a dataset obj**/ #define PRINTER_CONFIGURE_DATA_SET_ATTRIBS(DataSet, Attributes) \ if(NULL != DataSet && NULL != Attributes) { \ SetDataSetUserData(DataSet, (VOID *)Attributes); \ } \ /**Buffer output control (ON - prints directly to stdout, OFF - pushed into set buffer**/ #define PRINTER_CONFIGURE_BUFFERING(Ctx, State) \ if(NULL != Ctx) { \ Ctx->FormatTypeFlags.Flags.Buffered = State; \ } \ /**Print message directly to screen, status is ignored**/ #define PRINTER_PROMPT_MSG(ctx, efi_status, fmt, ...) \ do { \ EFI_STATUS rc; \ PRINTER_CONFIGURE_BUFFERING(ctx, OFF); \ if( EFI_SUCCESS != (rc = PrinterSetMsg(ctx, efi_status, fmt, ## __VA_ARGS__))) { \ NVDIMM_CRIT("Failed to prompt a message! (" FORMAT_EFI_STATUS ")", rc); \ } \ } while(0) /* Push message to the set buffer for later processing. * If error status will be saved as "last error". */ #define PRINTER_SET_MSG(ctx, efi_status, fmt, ...) \ do { \ EFI_STATUS rc; \ PRINTER_CONFIGURE_BUFFERING(ctx, ON); \ if( EFI_SUCCESS != (rc = PrinterSetMsg(ctx, efi_status, fmt, ## __VA_ARGS__))) { \ NVDIMM_CRIT("Failed to buffer a message! (" FORMAT_EFI_STATUS ")", rc); \ } \ \ } while(0) /**Print data (table or list) directly to stdout**/ #define PRINTER_PROMPT_DATA(ctx, efi_status, data) \ do { \ EFI_STATUS rc; \ PRINTER_CONFIGURE_BUFFERING(ctx, OFF); \ if( EFI_SUCCESS != (rc = PrinterSetData(ctx, efi_status, data))) { \ NVDIMM_CRIT("Failed to prompt data! (" FORMAT_EFI_STATUS ")", rc); \ } \ } while(0) /* Push dataset pointer to the set buffer for later processing. * If error status will be saved as "last error". * PRINTER_PROCESS_SET_BUFFER does not free the dataset. */ #define PRINTER_SET_DATA(ctx, efi_status, data) \ do { \ EFI_STATUS rc; \ PRINTER_CONFIGURE_BUFFERING(ctx, ON); \ if( EFI_SUCCESS != (rc = PrinterSetData(ctx, efi_status, data))) { \ NVDIMM_CRIT("Failed to buffer data! (" FORMAT_EFI_STATUS ")", rc); \ } \ } while(0) /**Transform the command status object to a string and print it directly to stdout**/ #define PRINTER_PROMPT_COMMAND_STATUS(ctx, efi_status, status_msg, status_preposition, command_status) \ do { \ EFI_STATUS rc; \ PRINTER_CONFIGURE_BUFFERING(ctx, OFF); \ if(EFI_SUCCESS != (rc = PrinterSetCommandStatus(ctx, efi_status, status_msg, status_preposition, command_status))) { \ NVDIMM_CRIT("Failed to prompt a command status object! (" FORMAT_EFI_STATUS ")", rc); \ } \ } while(0) /* Transform the command status obj to a string and store the result in the set buffer for later processing. * If error status will be saved as "last error". * PRINTER_PROCESS_SET_BUFFER free's the transformed string, but does not free the command status obj. */ #define PRINTER_SET_COMMAND_STATUS(ctx, efi_status, status_msg, status_preposition, command_status) \ do { \ EFI_STATUS rc; \ PRINTER_CONFIGURE_BUFFERING(ctx, ON); \ if(EFI_SUCCESS != (rc = PrinterSetCommandStatus(ctx, efi_status, status_msg, status_preposition, command_status))) { \ NVDIMM_CRIT("Failed to buffer a command status object! (" FORMAT_EFI_STATUS ")", rc); \ } \ } while(0) /**Transform all objects in the "set buffer" and print the result to stdout**/ #define PRINTER_PROCESS_SET_BUFFER(ctx) \ do { \ EFI_STATUS rc; \ if(EFI_SUCCESS != (rc = PrinterProcessSetBuffer(ctx))) { \ NVDIMM_CRIT("Failed to process printer objects! (" FORMAT_EFI_STATUS ")", rc); \ } \ } while (0) /**Transform all objects in the "set buffer" and print the result to stdout**/ #define PRINTER_PROCESS_SET_BUFFER_FORCE_TEXT_TABLE_MODE(ctx) \ do { \ EFI_STATUS rc; \ UINTN SavedFlags = 0; \ PRINT_FORMAT_TYPE SavedFormatType; \ SavedFormatType = ctx->FormatType; \ ctx->FormatType = TEXT; \ SavedFlags = ctx->FormatTypeFlags.Val; \ ctx->FormatTypeFlags.Val = 0; \ PRINTER_ENABLE_TEXT_TABLE_FORMAT(ctx); \ if( EFI_SUCCESS != (rc = PrinterProcessSetBuffer(ctx))) { \ NVDIMM_CRIT("Failed to process printer objects! (" FORMAT_EFI_STATUS ")", rc); \ } \ ctx->FormatTypeFlags.Val = SavedFlags; \ ctx->FormatType = SavedFormatType; \ } while (0) /* Helper that builds a path to a key's parent node which can be used with all SET_KEY_VAL* macros. * If *path will be freed if not NULL. */ #define PRINTER_BUILD_KEY_PATH(path, fmt, ...) \ do { \ FREE_POOL_SAFE(path); \ path = BuildPath(fmt, ## __VA_ARGS__); \ } while (0) /**Set a wide str into a dataset that resides in the "set buffer". If the value is NULL, nothing will be done**/ #define PRINTER_SET_KEY_VAL_WIDE_STR(ctx, key_path, key_name, val) \ do { \ if(NULL != (VOID*)((UINTN)val) || TRUE == gDisplayNulls) { \ EFI_STATUS rc; \ DATA_SET_CONTEXT *pDataSet = NULL; \ if( EFI_SUCCESS != (rc = LookupDataSet(ctx, key_path, &pDataSet))) { \ NVDIMM_CRIT("Failed to process printer objects! (" FORMAT_EFI_STATUS ")", rc); \ } \ if(NULL == (VOID*)((UINTN)val)){ \ gNullValuesEncounteredForDisplay++; \ } \ if( EFI_SUCCESS != (rc = SetKeyValueWideStr(pDataSet, key_name, (NULL == (VOID*)((UINTN)val) ? gNullValueToDisplay : val)))) { \ NVDIMM_CRIT("Failed to Set Key (%ls) Val (%ls) ReturnCode (" FORMAT_EFI_STATUS ")", key_name, val, rc); \ } \ } \ } while (0) /**Append a wide str into a dataset that resides in the "set buffer"**/ #define PRINTER_APPEND_KEY_VAL_WIDE_STR(ctx, key_path, key_name, val) \ do { \ EFI_STATUS rc; \ CHAR16 *prev_val = NULL; \ CHAR16 *concat_val = NULL; \ UINTN appendLen = 0; \ UINTN prevLen = 0; \ DATA_SET_CONTEXT *pDataSet = NULL; \ if( EFI_SUCCESS != (rc = LookupDataSet(ctx, key_path, &pDataSet))) { \ NVDIMM_CRIT("Failed to process printer objects! (" FORMAT_EFI_STATUS ")", rc); \ } \ if( EFI_SUCCESS != (rc = GetKeyValueWideStr(pDataSet, key_name, &prev_val, L""))) { \ NVDIMM_CRIT("Failed to Set Key (%ls) Val (%ls) ReturnCode (" FORMAT_EFI_STATUS ")", key_name, val, rc); \ } \ if(NULL != prev_val) { \ prevLen = StrLen(prev_val); \ } \ if(NULL != (CHAR16 *) val) { \ appendLen = StrLen(val); \ } \ concat_val = (CHAR16*)AllocateZeroPool((prevLen + appendLen + 1) * sizeof(CHAR16)); \ if(NULL != concat_val) { \ if(NULL != prev_val) { \ StrCpyS(concat_val, prevLen + appendLen + 1, prev_val); \ } \ if(NULL != (CHAR16 *) val) { \ StrCpyS(concat_val + prevLen, appendLen + 1, val); \ } \ } \ if( EFI_SUCCESS != (rc = SetKeyValueWideStr(pDataSet, key_name, concat_val))) { \ NVDIMM_CRIT("Failed to Set Key (%ls) Val (%ls) ReturnCode (" FORMAT_EFI_STATUS ")", key_name, val, rc); \ } \ FREE_POOL_SAFE(concat_val); \ } while (0) /**Set a wide str into a dataset that resides in the "set buffer"**/ #define PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(ctx, key_path, key_name, val, ...) \ do { \ if(NULL != (VOID*)((UINTN)val) || TRUE == gDisplayNulls) { \ EFI_STATUS rc; \ DATA_SET_CONTEXT *pDataSet = NULL; \ if( EFI_SUCCESS != (rc = LookupDataSet(ctx, key_path, &pDataSet))) { \ NVDIMM_CRIT("Failed to process printer objects! (" FORMAT_EFI_STATUS ")", rc); \ } \ if(NULL == (VOID*)((UINTN)val)){ \ gNullValuesEncounteredForDisplay++; \ } \ if( EFI_SUCCESS != (rc = SetKeyValueWideStrFormat(pDataSet, key_name, (NULL == (VOID*)((UINTN)val) ? gNullValueToDisplay : val), ## __VA_ARGS__))) { \ NVDIMM_CRIT("Failed to Set Key (%ls) Val (%ls) ReturnCode (" FORMAT_EFI_STATUS ")", key_name, val, rc); \ } \ } \ } while (0) /**Set a UINT16 into a dataset that resides in the "set buffer"**/ #define PRINTER_SET_KEY_VAL_UINT16(ctx, key_path, key_name, val, base) \ do { \ EFI_STATUS rc; \ DATA_SET_CONTEXT *pDataSet = NULL; \ if( EFI_SUCCESS != (rc = LookupDataSet(ctx, key_path, &pDataSet))) { \ NVDIMM_CRIT("Failed to process printer objects! (" FORMAT_EFI_STATUS ")", rc); \ } \ if( EFI_SUCCESS != (rc = SetKeyValueUint16(pDataSet, key_name, val, base))) { \ NVDIMM_CRIT("Failed to Set KeyVal pair (" FORMAT_EFI_STATUS ")", rc); \ } \ } while (0) /**Set a UINT64 into a dataset that resides in the "set buffer"**/ #define PRINTER_SET_KEY_VAL_UINT64(ctx, key_path, key_name, val, base) \ do { \ EFI_STATUS rc; \ DATA_SET_CONTEXT *pDataSet = NULL; \ if( EFI_SUCCESS != (rc = LookupDataSet(ctx, key_path, &pDataSet))) { \ NVDIMM_CRIT("Failed to process printer objects! (" FORMAT_EFI_STATUS ")", rc); \ } \ if( EFI_SUCCESS != (rc = SetKeyValueUint64(pDataSet, key_name, val, base))) { \ NVDIMM_CRIT("Failed to Set KeyVal pair (" FORMAT_EFI_STATUS ")", rc); \ } \ } while (0) /**Set a UINT8 into a dataset that resides in the "set buffer"**/ #define PRINTER_SET_KEY_VAL_UINT8(ctx, key_path, key_name, val, base) \ do { \ EFI_STATUS rc; \ DATA_SET_CONTEXT *pDataSet = NULL; \ if( EFI_SUCCESS != (rc = LookupDataSet(ctx, key_path, &pDataSet))) { \ NVDIMM_CRIT("Failed to process printer objects! (" FORMAT_EFI_STATUS ")", rc); \ } \ if( EFI_SUCCESS != (rc = SetKeyValueUint8(pDataSet, key_name, val, base))) { \ NVDIMM_CRIT("Failed to Set KeyVal pair (" FORMAT_EFI_STATUS ")", rc); \ } \ } while (0) /**Specify table attributes for a particular dataset**/ #define PRINTER_CONFIGURE_DATA_ATTRIBUTES(ctx, root_path, attribs) \ do { \ SetDataSetPrinterAttribs(ctx, root_path, (VOID *)attribs); \ } while (0) /* * Helper to lookup a dataset described by a path */ EFI_STATUS LookupDataSet( IN PRINT_CONTEXT *pPrintCtx, IN CHAR16 *pKeyPath, OUT DATA_SET_CONTEXT **pDataSet ); /* * Helper that associates a table/list of attributes with a dataset */ EFI_STATUS SetDataSetPrinterAttribs( IN PRINT_CONTEXT *pPrintCtx, IN CHAR16 *pKeyPath, IN PRINTER_DATA_SET_ATTRIBS *pAttribs ); /* * Helper that builds a path to a dataset node */ CHAR16 * EFIAPI BuildPath( CHAR16 *Format, ... ); /* * Create a printer context */ EFI_STATUS PrinterCreateCtx( OUT PRINT_CONTEXT **ppPrintCtx ); /* * Destroys a printer context */ EFI_STATUS PrinterDestroyCtx( IN PRINT_CONTEXT *pPrintCtx ); /* * Handle string messages */ EFI_STATUS EFIAPI PrinterSetMsg( IN PRINT_CONTEXT *pPrintCtx, IN EFI_STATUS Status, IN CHAR16 *pMsg, ... ); /* * Handle dataset objects */ EFI_STATUS PrinterSetData( IN PRINT_CONTEXT *pPrintCtx, IN EFI_STATUS Status, IN DATA_SET_CONTEXT *pDataSetCtx ); /* * Handle command status objects */ EFI_STATUS PrinterSetCommandStatus( IN PRINT_CONTEXT *pPrintCtx, OPTIONAL IN EFI_STATUS Status, IN CHAR16 *pStatusMessage, IN CHAR16 *pStatusPreposition, IN COMMAND_STATUS *pCommandStatus ); /* * Process all objects int the "set buffer" */ EFI_STATUS PrinterProcessSetBuffer( IN PRINT_CONTEXT *pPrintCtx ); #endif /** _PRINTER_H_**/ ipmctl-03.00.00.0485/DcpmPkg/common/ReadRunTimePreferences.c000066400000000000000000000052571440615110200231460ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "ReadRunTimePreferences.h" #ifdef OS_BUILD #include #else extern EFI_RUNTIME_SERVICES *gRT; #endif #include #include #include /** Retrieve the User Preferences from RunTime Services. @param[in, out] pDisplayPreferences pointer to the current driver preferences. @param[in] DisplayRequest enum of whether information from hii or cli will be displayed. @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS ReadRunTimePreferences( IN OUT DISPLAY_PREFERENCES *pDisplayPreferences, IN BOOLEAN DisplayRequest ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINTN VariableSize = 0; UINT16 CapacityUnit; NVDIMM_ENTRY(); if (NULL == pDisplayPreferences) { NVDIMM_DBG("One or more parameters are NULL"); goto Finish; } if (DISPLAY_HII_INFO == DisplayRequest) { CapacityUnit = FixedPcdGet16(PcdDcpmmHiiDefaultCapacityUnit); } else if (DISPLAY_CLI_INFO == DisplayRequest) { CapacityUnit = FixedPcdGet16(PcdDcpmmCliDefaultCapacityUnit); } else { NVDIMM_DBG("Invalid display information requested"); goto Finish; } ZeroMem(pDisplayPreferences, sizeof(*pDisplayPreferences)); VariableSize = sizeof(pDisplayPreferences->DimmIdentifier); ReturnCode = GET_VARIABLE( DISPLAY_DIMM_ID_VARIABLE_NAME, gNvmDimmVariableGuid, &VariableSize, &pDisplayPreferences->DimmIdentifier); if (EFI_NOT_FOUND == ReturnCode) { pDisplayPreferences->DimmIdentifier = DISPLAY_DIMM_ID_DEFAULT; ReturnCode = EFI_SUCCESS; } else if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to retrieve DimmID Display Variable"); goto Finish; } VariableSize = sizeof(pDisplayPreferences->SizeUnit); ReturnCode = GET_VARIABLE( DISPLAY_SIZE_VARIABLE_NAME, gNvmDimmVariableGuid, &VariableSize, &pDisplayPreferences->SizeUnit); if (EFI_NOT_FOUND == ReturnCode) { pDisplayPreferences->SizeUnit = CapacityUnit; ReturnCode = EFI_SUCCESS; } else if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to retrieve Size Display Variable"); goto Finish; } if (pDisplayPreferences->SizeUnit >= DISPLAY_SIZE_MAX_SIZE || pDisplayPreferences->DimmIdentifier >= DISPLAY_DIMM_ID_MAX_SIZE) { NVDIMM_DBG("Parameters retrieved from RT services are invalid, setting defaults"); pDisplayPreferences->SizeUnit = CapacityUnit; pDisplayPreferences->DimmIdentifier = DISPLAY_DIMM_ID_DEFAULT; ReturnCode = EFI_SUCCESS; goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/common/ReadRunTimePreferences.h000066400000000000000000000014701440615110200231440ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _READ_RUN_TIME_DISPLAY_PREFERENCES_H_ #define _READ_RUN_TIME_DISPLAY_PREFERENCES_H_ #include #include extern EFI_GUID gNvmDimmVariableGuid; enum DISPLAY_TYPE {DISPLAY_HII_INFO, DISPLAY_CLI_INFO}; /** Retrieve the User Preferences from RunTime Services. @param[in, out] pDisplayPreferences pointer to the current driver preferences. @param[in] DisplayRequest enum of whether information from hii or cli will be displayed. @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS ReadRunTimePreferences( IN OUT DISPLAY_PREFERENCES *pDisplayPreferences, IN BOOLEAN DisplayRequest ); #endif /** _READ_RUN_TIME_DISPLAY_PREFERENCES_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/ShowAcpi.c000066400000000000000000002104441440615110200203160ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include CHAR16 *pPath = NULL; UINT32 AcpiIndex = 0; CHAR16 *pTypePath = NULL; UINT32 TypeIndex = 0; ACPI_REVISION PcatRevision; GUID gDieTypeGuid = PMTT_TYPE_DIE_GUID; GUID gChannelTypeGuid = PMTT_TYPE_CHANNEL_GUID; GUID gSlotTypeGuid = PMTT_TYPE_SLOT_GUID; /** DecodePcatMemoryModeCapabilities - decodes the MemoryModeCapabilities field of PCAT structure type: PlatformCapabilityInfoTable @param[in] pPcatMemoryModeCapabilities pointer to MemoryModeCapabilities @retval NULL if no memory mode capabilities supported @retval comma separated string of supported capabilities **/ CHAR16* DecodePcatMemoryModeCapabilities( IN VOID *pPcatMemoryModeCapabilitiesField ) { CHAR16 *MemoryModeCapabilities = NULL; if (pPcatMemoryModeCapabilitiesField == NULL) { return MemoryModeCapabilities; } if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcatRevision)) { SUPPORTED_MEMORY_MODE *pPcatMemoryModeCapabilities = (SUPPORTED_MEMORY_MODE *)pPcatMemoryModeCapabilitiesField; if (pPcatMemoryModeCapabilities->MemoryModesFlags.OneLm) { MemoryModeCapabilities = CatSPrintClean(MemoryModeCapabilities, ((MemoryModeCapabilities == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"1LM"); } if (pPcatMemoryModeCapabilities->MemoryModesFlags.Memory) { MemoryModeCapabilities = CatSPrintClean(MemoryModeCapabilities, ((MemoryModeCapabilities == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"2LM"); } if (pPcatMemoryModeCapabilities->MemoryModesFlags.AppDirect) { MemoryModeCapabilities = CatSPrintClean(MemoryModeCapabilities, ((MemoryModeCapabilities == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"AppDirect"); } // Platforms with PCAT revision < 1.x always support mixed mode MemoryModeCapabilities = CatSPrintClean(MemoryModeCapabilities, ((MemoryModeCapabilities == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"MixedMode"); if (pPcatMemoryModeCapabilities->MemoryModesFlags.SubNUMACluster) { MemoryModeCapabilities = CatSPrintClean(MemoryModeCapabilities, ((MemoryModeCapabilities == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"SubNUMA Cluster"); } } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) { SUPPORTED_MEMORY_MODE3 *pPcatMemoryModeCapabilities = (SUPPORTED_MEMORY_MODE3 *)pPcatMemoryModeCapabilitiesField; if (pPcatMemoryModeCapabilities->MemoryModesFlags.OneLm) { MemoryModeCapabilities = CatSPrintClean(MemoryModeCapabilities, ((MemoryModeCapabilities == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"1LM"); } if (pPcatMemoryModeCapabilities->MemoryModesFlags.Memory) { MemoryModeCapabilities = CatSPrintClean(MemoryModeCapabilities, ((MemoryModeCapabilities == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"2LM"); } if (pPcatMemoryModeCapabilities->MemoryModesFlags.AppDirect) { MemoryModeCapabilities = CatSPrintClean(MemoryModeCapabilities, ((MemoryModeCapabilities == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"AppDirect"); } if (pPcatMemoryModeCapabilities->MemoryModesFlags.MixedMode) { MemoryModeCapabilities = CatSPrintClean(MemoryModeCapabilities, ((MemoryModeCapabilities == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"MixedMode"); } } if (MemoryModeCapabilities == NULL) { NVDIMM_DBG("DecodePcatMemoryModeCapabilities failed. Not enough resources!"); } return MemoryModeCapabilities; } /** DecodePcatCurrentMemoryMode - decodes the CurrentMemoryMode field of PCAT structure type: PlatformCapabilityInfoTable @param[in] pPcatCurrentMemoryMode pointer to CurrentMemoryMode @param[in] pPcatMemoryModeCapabilities pointer to MemoryModeCapabilities @retval NULL if no current memory mode set @retval string showing the current memory mode **/ CHAR16* DecodePcatCurrentMemoryMode( IN VOID *pPcatCurrentMemoryModeField, IN SUPPORTED_MEMORY_MODE *pPcatMemoryModeCapabilities ) { CHAR16 *CurrentMemoryMode = NULL; UINT8 CurrentVolatileMode = 0; UINT8 PersistentMode = 0; UINT8 AllowedVolatileMode = 0; UINT8 SubNUMACluster = 0; UINT8 mask = BIT0 | BIT1; if (pPcatCurrentMemoryModeField == NULL) { return CurrentMemoryMode; } if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcatRevision)) { CURRENT_MEMORY_MODE *pPcatCurrentMemoryMode = (CURRENT_MEMORY_MODE *)pPcatCurrentMemoryModeField; CurrentVolatileMode = pPcatCurrentMemoryMode->MemoryModeSplit.CurrentVolatileMode; PersistentMode = pPcatCurrentMemoryMode->MemoryModeSplit.PersistentMode; AllowedVolatileMode = pPcatCurrentMemoryMode->MemoryModeSplit.AllowedVolatileMode; SubNUMACluster = pPcatMemoryModeCapabilities->MemoryModesFlags.SubNUMACluster; } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) { CURRENT_MEMORY_MODE3 *pPcatCurrentMemoryMode = (CURRENT_MEMORY_MODE3 *)pPcatCurrentMemoryModeField; CurrentVolatileMode = pPcatCurrentMemoryMode->MemoryModeSplit.CurrentVolatileMode; PersistentMode = pPcatCurrentMemoryMode->MemoryModeSplit.PersistentMode; AllowedVolatileMode = pPcatCurrentMemoryMode->MemoryModeSplit.AllowedVolatileMode; } if ((CurrentVolatileMode & mask) == VOLATILE_MODE_1LM) { CurrentMemoryMode = CatSPrintClean(CurrentMemoryMode, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_STR, L"-Current Volatile Memory Mode", L"1LM"); } else if ((CurrentVolatileMode & mask) == VOLATILE_MODE_2LM) { CurrentMemoryMode = CatSPrintClean(CurrentMemoryMode, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_STR, L"-Current Volatile Memory Mode", L"2LM"); } else if ((CurrentVolatileMode & mask) == VOLATILE_MODE_1LM_PLUS_2LM) { CurrentMemoryMode = CatSPrintClean(CurrentMemoryMode, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_STR, L"-Current Volatile Memory Mode", L"1LM+2LM"); } if ((PersistentMode & mask) == PERSISTENT_MODE_DISABLED) { CurrentMemoryMode = CatSPrintClean(CurrentMemoryMode, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_STR, L"-Allowed Persistent Memory Mode", L"None"); } else if ((PersistentMode & mask) == PERSISTENT_MODE_APP_DIRECT) { CurrentMemoryMode = CatSPrintClean(CurrentMemoryMode, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_STR, L"-Allowed Persistent Memory Mode", L"AppDirect"); } if ((AllowedVolatileMode & mask) == VOLATILE_MODE_1LM) { CurrentMemoryMode = CatSPrintClean(CurrentMemoryMode, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_STR, L"-Allowed Volatile Memory Mode", L"1LM"); } else if ((AllowedVolatileMode & mask) == VOLATILE_MODE_1LM_OR_2LM) { CurrentMemoryMode = CatSPrintClean(CurrentMemoryMode, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_STR, L"-Allowed Volatile Memory Mode", L"1LM or 2LM"); } else if ((AllowedVolatileMode & mask) == VOLATILE_MODE_1LM_PLUS_2LM) { CurrentMemoryMode = CatSPrintClean(CurrentMemoryMode, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_STR, L"-Allowed Volatile Memory Mode", L"1LM+2LM"); } //Check if SubNUMA Cluster Mode is enabled if (SubNUMACluster && IS_ACPI_REV_MAJ_0_MIN_VALID(PcatRevision)) { CurrentMemoryMode = CatSPrintClean(CurrentMemoryMode, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR FORMAT_STR, L"-SubNUMA Cluster Mode ", (SubNUMACluster ? L"Enabled" : L"Disabled")); } if (CurrentMemoryMode == NULL) { NVDIMM_DBG("DecodePcatCurrentMemoryMode failed. Not enough resources!"); } return CurrentMemoryMode; } /** DecodePcatInterleaveFormatSupported - decodes the InterleaveFormatSupported field of PCAT structure type: MemoryInterleaveCapabilityTable @param[in] pPcatInterleaveFormatSupported pointer to InterleaveFormatSupported @retval NULL if no interleave information is present @retval string showing the interleave information **/ CHAR16* DecodePcatInterleaveFormatSupported( IN VOID *pPcatInterleaveFormatSupportedField, IN INTERLEAVE_SIZE *InterleaveSizeSupported ) { UINT16 mask2 = BIT0; UINT8 skip = BIT3 | BIT4 | BIT5; UINT8 index = 0; UINT8 NumOfBitsSet = 0; CHAR16 *InterleaveFormatSupported = NULL; CHAR16 *InterleaveSize[] = {L"64B", L"128B", L"256B", L"Reserved", L"Reserved", L"Reserved", L"4KB", L"Reserved"}; CHAR16 *NoOfChannelWays[] = {L"1-way", L"2-way", L"3-way", L"4-way", L"6-way", L"8-way", L"12-way", L"16-way", L"24-way"}; CHAR16 *ChannelWaysSupported = NULL; UINT32 ChannelInterleaveSize = 0; UINT32 ImcInterleaveSize = 0; UINT16 NumberOfChannelWays = 0; UINT32 Recommended = 0; if (pPcatInterleaveFormatSupportedField == NULL) { return InterleaveFormatSupported; } if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcatRevision)) { INTERLEAVE_FORMAT *pPcatInterleaveFormatSupported = (INTERLEAVE_FORMAT *)pPcatInterleaveFormatSupportedField; ChannelInterleaveSize = pPcatInterleaveFormatSupported->InterleaveFormatSplit.ChannelInterleaveSize; ImcInterleaveSize = pPcatInterleaveFormatSupported->InterleaveFormatSplit.iMCInterleaveSize; NumberOfChannelWays = pPcatInterleaveFormatSupported->InterleaveFormatSplit.NumberOfChannelWays & MAX_UINT16; Recommended = pPcatInterleaveFormatSupported->InterleaveFormatSplit.Recommended; } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) { INTERLEAVE_FORMAT3 *pPcatInterleaveFormatSupported = (INTERLEAVE_FORMAT3 *)pPcatInterleaveFormatSupportedField; ChannelInterleaveSize = InterleaveSizeSupported->InterleaveSizeSplit.ChannelInterleaveSize; ImcInterleaveSize = InterleaveSizeSupported->InterleaveSizeSplit.iMCInterleaveSize; CountNumOfBitsSet(pPcatInterleaveFormatSupported->InterleaveFormatSplit.InterleaveMap, &NumOfBitsSet); GetBitFieldForNumOfChannelWays(NumOfBitsSet, &NumberOfChannelWays); Recommended = pPcatInterleaveFormatSupported->InterleaveFormatSplit.Recommended; } //Check if the BIOS supported interleave format is recommended InterleaveFormatSupported = CatSPrintClean(InterleaveFormatSupported, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR, (Recommended ? L"-Recommended" : L"-Not recommended")); while (mask2 <= BIT6) { if (ChannelInterleaveSize & mask2) { InterleaveFormatSupported = CatSPrintClean(InterleaveFormatSupported, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_STR, L"-Channel interleave size", InterleaveSize[index]); break; } //Skip the reserved bits do { index++; mask2 <<= 1; } while ((mask2 & skip)); } mask2 = BIT0; index = 0; while (mask2 <= BIT6) { if (ImcInterleaveSize & mask2) { InterleaveFormatSupported = CatSPrintClean(InterleaveFormatSupported, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_STR, L"-iMC interleave size", InterleaveSize[index]); break; } //Skip the reserved bits do { index++; mask2 <<= 1; } while ((mask2 & skip)); } mask2 = BIT0; index = 0; while (mask2 <= BIT8) { if (NumberOfChannelWays & mask2) { ChannelWaysSupported = CatSPrintClean(ChannelWaysSupported, ((ChannelWaysSupported == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), NoOfChannelWays[index]); } index++; mask2 <<= 1; } if (ChannelWaysSupported != NULL) { InterleaveFormatSupported = CatSPrintClean(InterleaveFormatSupported, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_STR, L"-Channel ways", ChannelWaysSupported); } if (InterleaveFormatSupported == NULL) { NVDIMM_DBG("DecodePcatInterleaveFormatSupported failed. Not enough resources!"); } FREE_POOL_SAFE(ChannelWaysSupported); return InterleaveFormatSupported; } /** DecodePcatCacheCapabilities - decodes the CacheCapabilities field of PCAT structure type: PlatformCapabilityInfoTable @param[in] pPcatCacheCapabilitiesField pointer to CacheCapabilities @retval NULL if no interleave information is present @retval string showing the interleave information **/ CHAR16* DecodePcatCacheCapabilities( IN VOID *pPcatCacheCapabilitiesField ) { CHAR16 *CacheCapabilities = NULL; CHAR16 *MemoryModeCacheCapabilities = NULL; CACHE_CAPABILITIES *pPcatCacheCapabilities = (CACHE_CAPABILITIES *)pPcatCacheCapabilitiesField; if (pPcatCacheCapabilitiesField == NULL) { return CacheCapabilities; } if (pPcatCacheCapabilities->CacheCapabilitiesSplit.MemoryMode.InTile) { MemoryModeCacheCapabilities = CatSPrintClean(MemoryModeCacheCapabilities, ((MemoryModeCacheCapabilities == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"In-tile"); } if (pPcatCacheCapabilities->CacheCapabilitiesSplit.MemoryMode.CrossTile) { MemoryModeCacheCapabilities = CatSPrintClean(MemoryModeCacheCapabilities, ((MemoryModeCacheCapabilities == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"Cross-tile"); } CacheCapabilities = CatSPrintClean(CacheCapabilities, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_STR, L"-MemoryMode", ((MemoryModeCacheCapabilities == NULL) ? L"None" : MemoryModeCacheCapabilities)); FREE_POOL_SAFE(MemoryModeCacheCapabilities); return CacheCapabilities; } /** PrintPcatHeader - prints the header of the parsed NFit table. @param[in] pPcat pointer to the parsed PCAT header. @param[in] pointer to command's printer context. **/ VOID PrintAcpiHeader( IN TABLE_HEADER *pHeader, IN PRINT_CONTEXT *pPrinterCtx ) { if (pHeader == NULL) { NVDIMM_DBG("NULL Pointer provided"); return; } PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Signature", L"%c%c%c%c", ((UINT8 *)&pHeader->Signature)[0], ((UINT8 *)&pHeader->Signature)[1], ((UINT8 *)&pHeader->Signature)[2], ((UINT8 *)&pHeader->Signature)[3]); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Length", FORMAT_INT32 L" bytes", pHeader->Length); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Revision", FORMAT_HEX_NOWIDTH, pHeader->Revision.AsUint8); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"Checksum", FORMAT_HEX_NOWIDTH, pHeader->Checksum); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"OEMID", L"%c%c%c%c%c%c", pHeader->OemId[0], pHeader->OemId[1], pHeader->OemId[2], pHeader->OemId[3], pHeader->OemId[4], pHeader->OemId[5]); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"OEMTableID", L"%c%c%c%c%c%c%c%c", ((UINT8 *)&pHeader->OemTableId)[0], ((UINT8 *)&pHeader->OemTableId)[1], ((UINT8 *)&pHeader->OemTableId)[2], ((UINT8 *)&pHeader->OemTableId)[3], ((UINT8 *)&pHeader->OemTableId)[4], ((UINT8 *)&pHeader->OemTableId)[5], ((UINT8 *)&pHeader->OemTableId)[6], ((UINT8 *)&pHeader->OemTableId)[7]); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"OEMRevision", FORMAT_HEX_NOWIDTH, pHeader->OemRevision); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"CreatorID", L"%c%c%c%c", ((UINT8 *)&pHeader->CreatorId)[0], ((UINT8 *)&pHeader->CreatorId)[1], ((UINT8 *)&pHeader->CreatorId)[2], ((UINT8 *)&pHeader->CreatorId)[3]); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"CreatorRevision", FORMAT_HEX_NOWIDTH, pHeader->CreatorRevision); } /** PrintPcatTable - prints the subtable of the parsed PCAT table. @param[in] pTable pointer to the PCAT subtable. @param[in] pointer to command's printer context. **/ VOID PrintPcatTable( IN PCAT_TABLE_HEADER *pTable, IN PRINT_CONTEXT *pPrinterCtx ) { UINT16 Index = 0; CHAR16 *InterleaveFormatSupportedIndex = NULL; CHAR16 *InterleaveFormatSupported = NULL; CHAR16 *DcpmmMgmtSWConfigInputSupport = NULL; CHAR16 *MemoryModeCapabilities = NULL; CHAR16 *CurrentMemoryMode = NULL; CHAR16 *PersistentMemoryRasCapability = NULL; CHAR16 *MemoryMode[] = {L"1LM", L"2LM", L"Reserved", L"AppDirect", L"Reserved"}; CHAR16 *MaxPMInterleaveSets = NULL; CHAR16 *CacheCapabilities = NULL; if (pTable == NULL) { NVDIMM_DBG("NULL Pointer provided"); return; } RECONFIGURATION_INPUT_VALIDATION_INTERFACE_TABLE *pReconfInputValidationInterfaceTable = NULL; CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *pConfigManagementAttributesInfoTable = NULL; CHAR16 * pGuidStr = NULL; PRINTER_BUILD_KEY_PATH(pTypePath, DS_ACPITYPE_INDEX_PATH, AcpiIndex - 1, TypeIndex); TypeIndex++; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, ACPI_TYPE_STR, FORMAT_HEX_NOWIDTH, pTable->Type); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Length", FORMAT_INT32 L" bytes", pTable->Length); switch (pTable->Type) { case PCAT_TYPE_PLATFORM_CAPABILITY_INFO_TABLE: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, L"TypeEquals", L"PlatformCapabilityInfoTable"); if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcatRevision)) { PLATFORM_CAPABILITY_INFO *pPlatformCapabilityInfoTable = (PLATFORM_CAPABILITY_INFO *)pTable; if (pPlatformCapabilityInfoTable->MgmtSwConfigInputSupport & BIOS_SUPPORTS_CHANGING_CONFIG) { DcpmmMgmtSWConfigInputSupport = CatSPrintClean(DcpmmMgmtSWConfigInputSupport, FORMAT_STR, L"Yes"); } else { DcpmmMgmtSWConfigInputSupport = CatSPrintClean(DcpmmMgmtSWConfigInputSupport, FORMAT_STR, L"No"); } if (pPlatformCapabilityInfoTable->MgmtSwConfigInputSupport & BIOS_SUPPORTS_RUNTIME_INTERFACE) { DcpmmMgmtSWConfigInputSupport = CatSPrintClean(DcpmmMgmtSWConfigInputSupport, FORMAT_STR, L" & Runtime Interface for config validation"); } if (DcpmmMgmtSWConfigInputSupport != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"PMemModuleMgmtSWConfigInputSupport", FORMAT_HEX_NOWIDTH FORMAT_STR_WITH_PARENTHESIS, pPlatformCapabilityInfoTable->MgmtSwConfigInputSupport, DcpmmMgmtSWConfigInputSupport); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"PMemModuleMgmtSWConfigInputSupport", FORMAT_HEX_NOWIDTH, pPlatformCapabilityInfoTable->MgmtSwConfigInputSupport); } FREE_POOL_SAFE(DcpmmMgmtSWConfigInputSupport); MemoryModeCapabilities = DecodePcatMemoryModeCapabilities((VOID *)&pPlatformCapabilityInfoTable->MemoryModeCapabilities); if (MemoryModeCapabilities != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MemoryModeCapabilities", FORMAT_HEX_NOWIDTH FORMAT_STR_WITH_PARENTHESIS, pPlatformCapabilityInfoTable->MemoryModeCapabilities.MemoryModes, MemoryModeCapabilities); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MemoryModeCapabilities", FORMAT_HEX_NOWIDTH, pPlatformCapabilityInfoTable->MemoryModeCapabilities.MemoryModes); } FREE_POOL_SAFE(MemoryModeCapabilities); CurrentMemoryMode = DecodePcatCurrentMemoryMode((VOID *)&pPlatformCapabilityInfoTable->CurrentMemoryMode, &pPlatformCapabilityInfoTable->MemoryModeCapabilities); if (CurrentMemoryMode != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"CurrentMemoryMode", FORMAT_HEX_NOWIDTH FORMAT_STR, pPlatformCapabilityInfoTable->CurrentMemoryMode.MemoryMode, CurrentMemoryMode); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"CurrentMemoryMode", FORMAT_HEX_NOWIDTH, pPlatformCapabilityInfoTable->CurrentMemoryMode.MemoryMode); } FREE_POOL_SAFE(CurrentMemoryMode); if (pPlatformCapabilityInfoTable->PersistentMemoryRasCapability & PERSISTENT_MEMORY_REGION_MIRRORING) { PersistentMemoryRasCapability = CatSPrintClean(PersistentMemoryRasCapability, ((PersistentMemoryRasCapability == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"Mirroring"); } if (pPlatformCapabilityInfoTable->PersistentMemoryRasCapability & PERSISTENT_MEMORY_REGION_SPARE) { PersistentMemoryRasCapability = CatSPrintClean(PersistentMemoryRasCapability, ((PersistentMemoryRasCapability == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"Spare"); } if (pPlatformCapabilityInfoTable->PersistentMemoryRasCapability & PERSISTENT_MEMORY_REGION_MIGRATION) { PersistentMemoryRasCapability = CatSPrintClean(PersistentMemoryRasCapability, ((PersistentMemoryRasCapability == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), L"Migration"); } if (PersistentMemoryRasCapability != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"PersistentMemoryRASCapability", FORMAT_HEX_NOWIDTH FORMAT_STR_WITH_PARENTHESIS L"\n", pPlatformCapabilityInfoTable->PersistentMemoryRasCapability, PersistentMemoryRasCapability); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"PersistentMemoryRASCapability", FORMAT_HEX_NOWIDTH L"\n", pPlatformCapabilityInfoTable->PersistentMemoryRasCapability); } FREE_POOL_SAFE(PersistentMemoryRasCapability); } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) { PLATFORM_CAPABILITY_INFO3 *pPlatformCapabilityInfoTable = (PLATFORM_CAPABILITY_INFO3 *)pTable; if (pPlatformCapabilityInfoTable->MgmtSwConfigInputSupport & BIOS_SUPPORTS_CHANGING_CONFIG) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"PMemModuleMgmtSWConfigInputSupport", FORMAT_HEX_NOWIDTH FORMAT_STR_WITH_PARENTHESIS, pPlatformCapabilityInfoTable->MgmtSwConfigInputSupport, L"Yes"); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"PMemModuleMgmtSWConfigInputSupport", FORMAT_HEX_NOWIDTH FORMAT_STR_WITH_PARENTHESIS, pPlatformCapabilityInfoTable->MgmtSwConfigInputSupport, L"No"); } MemoryModeCapabilities = DecodePcatMemoryModeCapabilities((VOID *)&pPlatformCapabilityInfoTable->MemoryModeCapabilities); if (MemoryModeCapabilities != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MemoryModeCapabilities", FORMAT_HEX_NOWIDTH FORMAT_STR_WITH_PARENTHESIS, pPlatformCapabilityInfoTable->MemoryModeCapabilities.MemoryModes, MemoryModeCapabilities); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MemoryModeCapabilities", FORMAT_HEX_NOWIDTH, pPlatformCapabilityInfoTable->MemoryModeCapabilities.MemoryModes); } FREE_POOL_SAFE(MemoryModeCapabilities); CurrentMemoryMode = DecodePcatCurrentMemoryMode((VOID *)&pPlatformCapabilityInfoTable->CurrentMemoryMode, NULL); if (CurrentMemoryMode != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"CurrentMemoryMode", FORMAT_HEX_NOWIDTH FORMAT_STR, pPlatformCapabilityInfoTable->CurrentMemoryMode.MemoryMode, CurrentMemoryMode); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"CurrentMemoryMode", FORMAT_HEX_NOWIDTH, pPlatformCapabilityInfoTable->CurrentMemoryMode.MemoryMode); } FREE_POOL_SAFE(CurrentMemoryMode); MaxPMInterleaveSets = CatSPrintClean(MaxPMInterleaveSets, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_HEX, L"-Per CPU Die", pPlatformCapabilityInfoTable->MaxPMInterleaveSets.MaxInterleaveSetsSplit.PerDie); MaxPMInterleaveSets = CatSPrintClean(MaxPMInterleaveSets, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_HEX, L"-Per PMem module", pPlatformCapabilityInfoTable->MaxPMInterleaveSets.MaxInterleaveSetsSplit.PerDcpmm); if (MaxPMInterleaveSets != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MaxPMInterleaveSets", FORMAT_HEX_NOWIDTH FORMAT_STR , pPlatformCapabilityInfoTable->MaxPMInterleaveSets.AsUint16, MaxPMInterleaveSets); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MaxPMInterleaveSets", FORMAT_HEX_NOWIDTH , pPlatformCapabilityInfoTable->MaxPMInterleaveSets.AsUint16); } FREE_POOL_SAFE(MaxPMInterleaveSets); if (IS_ACPI_REV_MAJ_3_MIN_VALID(PcatRevision)) { CacheCapabilities = DecodePcatCacheCapabilities((VOID *)&pPlatformCapabilityInfoTable->CacheCapabilities); if (CacheCapabilities != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"CacheCapabilities", FORMAT_HEX_NOWIDTH FORMAT_STR , pPlatformCapabilityInfoTable->CacheCapabilities.AsUint8, CacheCapabilities); } FREE_POOL_SAFE(CacheCapabilities); } PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"DDRCacheSize", FORMAT_UINT32 L" GiB per DDR DIMM\n", pPlatformCapabilityInfoTable->DDRCacheSize); } break; case PCAT_TYPE_INTERLEAVE_CAPABILITY_INFO_TABLE: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, L"TypeEquals", L"MemoryInterleaveCapabilityTable"); if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcatRevision)) { MEMORY_INTERLEAVE_CAPABILITY_INFO *pMemoryInterleaveCapabilityInfoTable = (MEMORY_INTERLEAVE_CAPABILITY_INFO *)pTable; if (pMemoryInterleaveCapabilityInfoTable->MemoryMode <= 4) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MemoryMode", FORMAT_HEX_NOWIDTH FORMAT_STR_WITH_PARENTHESIS, pMemoryInterleaveCapabilityInfoTable->MemoryMode, MemoryMode[pMemoryInterleaveCapabilityInfoTable->MemoryMode]); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MemoryMode", FORMAT_HEX_NOWIDTH, pMemoryInterleaveCapabilityInfoTable->MemoryMode); } PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NumberOfInterleaveFormatsSupported", FORMAT_HEX_NOWIDTH, pMemoryInterleaveCapabilityInfoTable->NumOfFormatsSupported); for (Index = 0; Index < pMemoryInterleaveCapabilityInfoTable->NumOfFormatsSupported; Index++) { InterleaveFormatSupportedIndex = CatSPrint(NULL, L"InterleaveFormatSupported(%d)", Index); InterleaveFormatSupported = DecodePcatInterleaveFormatSupported((VOID *)&pMemoryInterleaveCapabilityInfoTable->InterleaveFormatList[Index], NULL); if (InterleaveFormatSupported != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, InterleaveFormatSupportedIndex, FORMAT_HEX_NOWIDTH FORMAT_STR, pMemoryInterleaveCapabilityInfoTable->InterleaveFormatList[Index], InterleaveFormatSupported); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, InterleaveFormatSupportedIndex, FORMAT_HEX_NOWIDTH, pMemoryInterleaveCapabilityInfoTable->InterleaveFormatList[Index]); } FREE_POOL_SAFE(InterleaveFormatSupportedIndex); FREE_POOL_SAFE(InterleaveFormatSupported); } PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"InterleaveAlignmentSize", FORMAT_HEX_NOWIDTH L"\n", pMemoryInterleaveCapabilityInfoTable->InterleaveAlignmentSize); } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) { MEMORY_INTERLEAVE_CAPABILITY_INFO3 *pMemoryInterleaveCapabilityInfoTable = (MEMORY_INTERLEAVE_CAPABILITY_INFO3 *)pTable; if (pMemoryInterleaveCapabilityInfoTable->MemoryMode <= 4) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MemoryMode", FORMAT_HEX_NOWIDTH FORMAT_STR_WITH_PARENTHESIS, pMemoryInterleaveCapabilityInfoTable->MemoryMode, MemoryMode[pMemoryInterleaveCapabilityInfoTable->MemoryMode]); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MemoryMode", FORMAT_HEX_NOWIDTH, pMemoryInterleaveCapabilityInfoTable->MemoryMode); } PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NumberOfInterleaveFormatsSupported", FORMAT_HEX_NOWIDTH, pMemoryInterleaveCapabilityInfoTable->NumOfFormatsSupported); for (Index = 0; Index < pMemoryInterleaveCapabilityInfoTable->NumOfFormatsSupported; Index++) { InterleaveFormatSupportedIndex = CatSPrint(NULL, L"InterleaveFormatSupported(%d)", Index); InterleaveFormatSupported = DecodePcatInterleaveFormatSupported((VOID *)&pMemoryInterleaveCapabilityInfoTable->InterleaveFormatList[Index], &pMemoryInterleaveCapabilityInfoTable->InterleaveSize); if (InterleaveFormatSupported != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, InterleaveFormatSupportedIndex, FORMAT_HEX_NOWIDTH FORMAT_STR, pMemoryInterleaveCapabilityInfoTable->InterleaveFormatList[Index], InterleaveFormatSupported); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, InterleaveFormatSupportedIndex, FORMAT_HEX_NOWIDTH, pMemoryInterleaveCapabilityInfoTable->InterleaveFormatList[Index]); } FREE_POOL_SAFE(InterleaveFormatSupportedIndex); FREE_POOL_SAFE(InterleaveFormatSupported); } PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"InterleaveAlignmentSize", FORMAT_HEX_NOWIDTH, pMemoryInterleaveCapabilityInfoTable->InterleaveAlignmentSize); MaxPMInterleaveSets = CatSPrintClean(MaxPMInterleaveSets, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_HEX, L"-Per CPU Die", pMemoryInterleaveCapabilityInfoTable->MaxInterleaveSetsPerMemType.MaxInterleaveSetsSplit.PerDie); MaxPMInterleaveSets = CatSPrintClean(MaxPMInterleaveSets, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR_COLON_SPACE_HEX, L"-Per PMem module", pMemoryInterleaveCapabilityInfoTable->MaxInterleaveSetsPerMemType.MaxInterleaveSetsSplit.PerDcpmm); if (MaxPMInterleaveSets != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MaxPMInterleaveSetsPerMemType", FORMAT_HEX_NOWIDTH FORMAT_STR L"\n", pMemoryInterleaveCapabilityInfoTable->MaxInterleaveSetsPerMemType.AsUint16, MaxPMInterleaveSets); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MaxPMInterleaveSetsPerMemType", FORMAT_HEX_NOWIDTH L"\n", pMemoryInterleaveCapabilityInfoTable->MaxInterleaveSetsPerMemType.AsUint16); } FREE_POOL_SAFE(MaxPMInterleaveSets); } break; case PCAT_TYPE_RUNTIME_INTERFACE_TABLE: pReconfInputValidationInterfaceTable = (RECONFIGURATION_INPUT_VALIDATION_INTERFACE_TABLE *) pTable; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, L"TypeEquals", L"Re-configurationInputValidationInterfaceTable"); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"AddressSpaceID", FORMAT_HEX_NOWIDTH, pReconfInputValidationInterfaceTable->AddressSpaceId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"BitWidth", FORMAT_HEX_NOWIDTH, pReconfInputValidationInterfaceTable->BitWidth); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"BitOffset", FORMAT_HEX_NOWIDTH, pReconfInputValidationInterfaceTable->BitOffset); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"AccessSize", FORMAT_HEX_NOWIDTH, pReconfInputValidationInterfaceTable->AccessSize); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Address", FORMAT_UINT64_HEX_NOWIDTH, pReconfInputValidationInterfaceTable->Address); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"OperationType", FORMAT_HEX_NOWIDTH, pReconfInputValidationInterfaceTable->TriggerOperationType); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Value", FORMAT_HEX_NOWIDTH, pReconfInputValidationInterfaceTable->TriggerValue); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Mask", FORMAT_HEX_NOWIDTH, pReconfInputValidationInterfaceTable->TriggerMask); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"GASStructure", FORMAT_STR, pReconfInputValidationInterfaceTable->GasStructure[0] == 0 ? L"System Memory" : L"Unknown"); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"OperationType", FORMAT_HEX_NOWIDTH, pReconfInputValidationInterfaceTable->StatusOperationType); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Mask", FORMAT_HEX_NOWIDTH L"\n", pReconfInputValidationInterfaceTable->StatusMask); break; case PCAT_TYPE_CONFIG_MANAGEMENT_ATTRIBUTES_TABLE: pConfigManagementAttributesInfoTable = (CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *) pTable; pGuidStr = GuidToStr(&pConfigManagementAttributesInfoTable->Guid); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, L"TypeEquals", L"ConfigurationManagementAttributesExtensionTable"); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"VendorID", FORMAT_HEX_NOWIDTH, pConfigManagementAttributesInfoTable->VendorId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"GUID", FORMAT_STR, pGuidStr); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"GUIDDataPointer", FORMAT_POINTER L"\n", pConfigManagementAttributesInfoTable->pGuidData); FREE_POOL_SAFE(pGuidStr); break; case PCAT_TYPE_SOCKET_SKU_INFO_TABLE: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, L"TypeEquals", L"SocketSkuInfoTable"); if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcatRevision)) { SOCKET_SKU_INFO_TABLE *pSocketSkuInfoTable = (SOCKET_SKU_INFO_TABLE *)pTable; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SocketID", FORMAT_HEX_NOWIDTH, pSocketSkuInfoTable->SocketId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MappedMemorySizeLimit", FORMAT_UINT64, pSocketSkuInfoTable->MappedMemorySizeLimit); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"TotalMemorySizeMappedToSpa", FORMAT_UINT64, pSocketSkuInfoTable->TotalMemorySizeMappedToSpa); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"CachingMemorySize", FORMAT_UINT64 L"\n", pSocketSkuInfoTable->CachingMemorySize); } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) { DIE_SKU_INFO_TABLE *pDieSkuInfoTable = (DIE_SKU_INFO_TABLE *)pTable; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SocketID", FORMAT_HEX_NOWIDTH, pDieSkuInfoTable->SocketId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"DieID", FORMAT_HEX_NOWIDTH, pDieSkuInfoTable->DieId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MappedMemorySizeLimit", FORMAT_UINT64, pDieSkuInfoTable->MappedMemorySizeLimit); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"TotalMemorySizeMappedToSpa", FORMAT_UINT64, pDieSkuInfoTable->TotalMemorySizeMappedToSpa); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"CachingMemorySize", FORMAT_UINT64 L"\n", pDieSkuInfoTable->CachingMemorySize); } break; default: break; } } /** PrintPcat - prints the header and all of the tables in the parsed PCAT table. @param[in] pPcat pointer to the parsed PCAT. @param[in] pointer to command's printer context. **/ VOID PrintPcat( IN ParsedPcatHeader *pPcat, IN PRINT_CONTEXT *pPrinterCtx ) { UINT32 Index = 0; if (pPcat == NULL) { NVDIMM_DBG("NULL Pointer provided"); return; } PRINTER_BUILD_KEY_PATH(pPath, DS_ACPI_INDEX_PATH, AcpiIndex); AcpiIndex++; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SYSTEM_TARGET_STR, L"Platform Configurations Attributes Table"); PcatRevision = pPcat->pPlatformConfigAttr->Header.Revision; PrintAcpiHeader(&pPcat->pPlatformConfigAttr->Header, pPrinterCtx); for (Index = 0; Index < pPcat->PlatformCapabilityInfoNum; Index++) { if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcatRevision)) { if (pPcat->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo[Index] == NULL) { return; } PrintPcatTable((PCAT_TABLE_HEADER *)pPcat->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo[Index], pPrinterCtx); } if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) { if (pPcat->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[Index] == NULL) { return; } PrintPcatTable((PCAT_TABLE_HEADER *)pPcat->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[Index], pPrinterCtx); } } for (Index = 0; Index < pPcat->MemoryInterleaveCapabilityInfoNum; Index++) { if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcatRevision)) { if (pPcat->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo[Index] == NULL) { return; } PrintPcatTable((PCAT_TABLE_HEADER *)pPcat->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo[Index], pPrinterCtx); } if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) { if (pPcat->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo[Index] == NULL) { return; } PrintPcatTable((PCAT_TABLE_HEADER *)pPcat->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo[Index], pPrinterCtx); } } for (Index = 0; Index < pPcat->RuntimeInterfaceValConfInputNum; Index++) { if (pPcat->ppRuntimeInterfaceValConfInput[Index] == NULL) { return; } PrintPcatTable((PCAT_TABLE_HEADER *) pPcat->ppRuntimeInterfaceValConfInput[Index], pPrinterCtx); } for (Index = 0; Index < pPcat->ConfigManagementAttributesInfoNum; Index++) { if (pPcat->ppConfigManagementAttributesInfo[Index] == NULL) { return; } PrintPcatTable((PCAT_TABLE_HEADER *) pPcat->ppConfigManagementAttributesInfo[Index], pPrinterCtx); } for (Index = 0; Index < pPcat->SocketSkuInfoNum; Index++) { if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcatRevision)) { if (pPcat->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable[Index] == NULL) { return; } PrintPcatTable((PCAT_TABLE_HEADER *)pPcat->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable[Index], pPrinterCtx); } if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) { if (pPcat->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index] == NULL) { return; } PrintPcatTable((PCAT_TABLE_HEADER *)pPcat->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index], pPrinterCtx); } } FREE_POOL_SAFE(pPath); FREE_POOL_SAFE(pTypePath); } /** DecodeNfitNvDimmStateFlags - decodes the NvDimmStateFlags field of NFIT structure type: NvDimmRegion @param[in] NvDimmStateFlags field from NFIT table to be decoded @retval string showing information about events & operation status based on flags set **/ CHAR16* DecodeNfitNvDimmStateFlags( IN UINT16 nfitNvDimmStateFlags ) { UINT8 mask = BIT0; CHAR16 *NvDimmStateFlags = NULL; while (mask <= BIT6) { switch (mask) { case NVDIMM_STATE_FLAGS_SAVE: if (nfitNvDimmStateFlags & mask) { NvDimmStateFlags = CatSPrintClean(NvDimmStateFlags, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR SPACE_FORMAT_HEX, L"-Save failed", NVDIMM_STATE_FLAGS_SAVE); } break; case NVDIMM_STATE_FLAGS_RESTORE: if (nfitNvDimmStateFlags & mask) { NvDimmStateFlags = CatSPrintClean(NvDimmStateFlags, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR SPACE_FORMAT_HEX, L"-Restore failed", NVDIMM_STATE_FLAGS_RESTORE); } break; case NVDIMM_STATE_FLAGS_FLUSH: if (nfitNvDimmStateFlags & mask) { NvDimmStateFlags = CatSPrintClean(NvDimmStateFlags, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR SPACE_FORMAT_HEX, L"-Flush failed", NVDIMM_STATE_FLAGS_FLUSH); } break; case NVDIMM_STATE_FLAGS_REGION_ARMED: if (nfitNvDimmStateFlags & mask) { NvDimmStateFlags = CatSPrintClean(NvDimmStateFlags, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR SPACE_FORMAT_HEX, L"-PM writes disabled or Not armed or Previous ERASE failed", NVDIMM_STATE_FLAGS_REGION_ARMED); } break; case NVDIMM_STATE_FLAGS_EVENTS_OBSERVED: if (nfitNvDimmStateFlags & mask) { NvDimmStateFlags = CatSPrintClean(NvDimmStateFlags, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR SPACE_FORMAT_HEX, L"-Smart & Health events prior to OSPM handoff", NVDIMM_STATE_FLAGS_EVENTS_OBSERVED); } break; case NVDIMM_STATE_FLAGS_EVENTS_NOTIFY: if (nfitNvDimmStateFlags & mask) { NvDimmStateFlags = CatSPrintClean(NvDimmStateFlags, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR SPACE_FORMAT_HEX, L"-Notify OSPM of Smart & Health events", NVDIMM_STATE_FLAGS_EVENTS_NOTIFY); } break; case NVDIMM_STATE_FLAGS_NOT_MAPPED: if (nfitNvDimmStateFlags & mask) { NvDimmStateFlags = CatSPrintClean(NvDimmStateFlags, L"\n" SHOW_LIST_IDENT SHOW_LIST_IDENT SHOW_LIST_IDENT FORMAT_STR SPACE_FORMAT_HEX, L"-NVDIMM region not mapped into SPA range", NVDIMM_STATE_FLAGS_NOT_MAPPED); } break; } mask <<= 1; } if (NvDimmStateFlags == NULL) { NVDIMM_DBG("DecodePcatMemoryModeCapabilities failed. Not enough resources!"); } return NvDimmStateFlags; } /** PrintFitTable - prints the subtable of the parsed NFit table. @param[in] pTable pointer to the NFit subtable. @param[in] pointer to command's printer context. **/ VOID PrintFitTable( IN SubTableHeader *pTable, IN PRINT_CONTEXT *pPrinterCtx ) { SpaRangeTbl *pTableSpaRange = NULL; NvDimmRegionMappingStructure *pTableNvDimmRegion = NULL; InterleaveStruct *pTableInterleave = NULL; ControlRegionTbl *pTableControlRegion = NULL; BWRegionTbl *pTableBWRegion = NULL; FlushHintTbl *pTableFlushHint = NULL; PlatformCapabilitiesTbl *pTablePlatCap = NULL; UINT32 Index = 0; CHAR16 *pGuidStr = NULL; CHAR16 *LineOffset = NULL; CHAR16 *FlushHintAddress = NULL; CHAR16 *NvDimmStateFlags = NULL; if (pTable == NULL) { return; } PRINTER_BUILD_KEY_PATH(pTypePath, DS_ACPITYPE_INDEX_PATH, AcpiIndex - 1, TypeIndex); TypeIndex++; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, ACPI_TYPE_STR, FORMAT_HEX_NOWIDTH, pTable->Type); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Length", FORMAT_INT32 L" bytes", pTable->Length); switch (pTable->Type) { case NVDIMM_SPA_RANGE_TYPE: pTableSpaRange = (SpaRangeTbl *)pTable; pGuidStr = GuidToStr(&pTableSpaRange->AddressRangeTypeGuid); PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, L"TypeEquals", L"SpaRange"); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"AddressRangeType", FORMAT_STR, pGuidStr); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SpaRangeDescriptionTableIndex", FORMAT_HEX_NOWIDTH, pTableSpaRange->SpaRangeDescriptionTableIndex); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Flags", FORMAT_HEX_NOWIDTH, pTableSpaRange->Flags); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"ProximityDomain", FORMAT_HEX_NOWIDTH, pTableSpaRange->ProximityDomain); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SystemPhysicalAddressRangeBase", FORMAT_UINT64_HEX_NOWIDTH, pTableSpaRange->SystemPhysicalAddressRangeBase); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SystemPhysicalAddressRangeLength", FORMAT_UINT64_HEX_NOWIDTH, pTableSpaRange->SystemPhysicalAddressRangeLength); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MemoryMappingAttribute", FORMAT_UINT64_HEX_NOWIDTH L"\n", pTableSpaRange->AddressRangeMemoryMappingAttribute); FREE_POOL_SAFE(pGuidStr); break; case NVDIMM_NVDIMM_REGION_TYPE: pTableNvDimmRegion = (NvDimmRegionMappingStructure *)pTable; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, L"TypeEquals", L"NvDimmRegion"); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NfitDeviceHandle", FORMAT_HEX, pTableNvDimmRegion->DeviceHandle.AsUint32); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NfitDeviceHandle.DimmNumber", FORMAT_HEX_NOWIDTH, pTableNvDimmRegion->DeviceHandle.NfitDeviceHandle.DimmNumber); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NfitDeviceHandle.MemChannel", FORMAT_HEX_NOWIDTH, pTableNvDimmRegion->DeviceHandle.NfitDeviceHandle.MemChannel); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NfitDeviceHandle.MemControllerId", FORMAT_HEX_NOWIDTH, pTableNvDimmRegion->DeviceHandle.NfitDeviceHandle.MemControllerId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NfitDeviceHandle.SocketId", FORMAT_HEX_NOWIDTH, pTableNvDimmRegion->DeviceHandle.NfitDeviceHandle.SocketId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NfitDeviceHandle.NodeControllerId", FORMAT_HEX_NOWIDTH, pTableNvDimmRegion->DeviceHandle.NfitDeviceHandle.NodeControllerId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NvDimmPhysicalId", FORMAT_HEX_NOWIDTH, pTableNvDimmRegion->NvDimmPhysicalId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NvDimmRegionalId", FORMAT_HEX_NOWIDTH, pTableNvDimmRegion->NvDimmRegionalId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SpaRangeDescriptionTableIndex", FORMAT_HEX_NOWIDTH, pTableNvDimmRegion->SpaRangeDescriptionTableIndex); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NvdimmControlRegionDescriptorTableIndex", FORMAT_HEX_NOWIDTH, pTableNvDimmRegion->NvdimmControlRegionDescriptorTableIndex); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NvDimmRegionSize", FORMAT_UINT64_HEX_NOWIDTH, pTableNvDimmRegion->NvDimmRegionSize); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"RegionOffset", FORMAT_UINT64_HEX_NOWIDTH, pTableNvDimmRegion->RegionOffset); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NvDimmPhysicalAddressRegionBase", FORMAT_UINT64_HEX_NOWIDTH, pTableNvDimmRegion->NvDimmPhysicalAddressRegionBase); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"InterleaveStructureIndex", FORMAT_HEX_NOWIDTH, pTableNvDimmRegion->InterleaveStructureIndex); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"InterleaveWays", FORMAT_HEX_NOWIDTH, pTableNvDimmRegion->InterleaveWays); NvDimmStateFlags = DecodeNfitNvDimmStateFlags(pTableNvDimmRegion->NvDimmStateFlags); if (NvDimmStateFlags != NULL) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NvDimmStateFlags", FORMAT_HEX FORMAT_STR_NL, pTableNvDimmRegion->NvDimmStateFlags, NvDimmStateFlags); } else { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NvDimmStateFlags", FORMAT_HEX L"\n", pTableNvDimmRegion->NvDimmStateFlags); } FREE_POOL_SAFE(NvDimmStateFlags); break; case NVDIMM_INTERLEAVE_TYPE: pTableInterleave = (InterleaveStruct *)pTable; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, L"TypeEquals", L"Interleave"); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"InterleaveStructureIndex", FORMAT_HEX_NOWIDTH, pTableInterleave->InterleaveStructureIndex); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NumberOfLinesDescribed", FORMAT_HEX_NOWIDTH, pTableInterleave->NumberOfLinesDescribed); for (Index = 0; Index < pTableInterleave->NumberOfLinesDescribed; Index++) { LineOffset = CatSPrint(NULL, L"LineOffset %d", Index); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, LineOffset, FORMAT_HEX_NOWIDTH, pTableInterleave->LinesOffsets[Index]); FREE_POOL_SAFE(LineOffset); } PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"LineSize", FORMAT_HEX_NOWIDTH L"\n", pTableInterleave->LineSize); break; case NVDIMM_SMBIOS_MGMT_INFO_TYPE: PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, L"TypeEquals", L"Smbios\n"); break; case NVDIMM_CONTROL_REGION_TYPE: pTableControlRegion = (ControlRegionTbl *)pTable; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, L"TypeEquals", L"ControlRegion"); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"ControlRegionDescriptorTableIndex", FORMAT_HEX_NOWIDTH, pTableControlRegion->ControlRegionDescriptorTableIndex); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"VendorId", FORMAT_HEX_NOWIDTH, pTableControlRegion->VendorId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"DeviceId", FORMAT_HEX_NOWIDTH, pTableControlRegion->DeviceId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Rid", FORMAT_HEX_NOWIDTH, pTableControlRegion->Rid); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SubsystemVendorId", FORMAT_HEX_NOWIDTH, pTableControlRegion->SubsystemVendorId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SubsystemDeviceId", FORMAT_HEX_NOWIDTH, pTableControlRegion->SubsystemDeviceId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SubsystemRid", FORMAT_HEX_NOWIDTH, pTableControlRegion->SubsystemRid); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"ValidFields", FORMAT_HEX_NOWIDTH, pTableControlRegion->ValidFields); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"ManufacturingLocation", FORMAT_HEX_NOWIDTH, pTableControlRegion->ManufacturingLocation); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"ManufacturingDate", FORMAT_HEX_NOWIDTH, pTableControlRegion->ManufacturingDate); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SerialNumber", FORMAT_HEX_NOWIDTH, pTableControlRegion->SerialNumber); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"RegionFormatInterfaceCode", FORMAT_HEX_NOWIDTH, pTableControlRegion->RegionFormatInterfaceCode); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NumberOfBlockControlWindows", FORMAT_HEX_NOWIDTH L"\n", pTableControlRegion->NumberOfBlockControlWindows); if (pTableControlRegion->NumberOfBlockControlWindows > 0) { PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SizeOfBlockControlWindow", FORMAT_UINT64_HEX_NOWIDTH, pTableControlRegion->SizeOfBlockControlWindow); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"CommandRegisterOffsetInBlockControlWindow", FORMAT_UINT64_HEX_NOWIDTH, pTableControlRegion->CommandRegisterOffsetInBlockControlWindow); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SizeOfCommandRegisterInBlockControlWindows", FORMAT_UINT64_HEX_NOWIDTH, pTableControlRegion->SizeOfCommandRegisterInBlockControlWindows); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"StatusRegisterOffsetInBlockControlWindow", FORMAT_UINT64_HEX_NOWIDTH, pTableControlRegion->StatusRegisterOffsetInBlockControlWindow); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SizeOfStatusRegisterInBlockControlWindows", FORMAT_UINT64_HEX_NOWIDTH, pTableControlRegion->SizeOfStatusRegisterInBlockControlWindows); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"ControlRegionFlag", FORMAT_HEX_NOWIDTH L"\n", pTableControlRegion->ControlRegionFlag); } break; case NVDIMM_BW_DATA_WINDOW_REGION_TYPE: pTableBWRegion = (BWRegionTbl *)pTable; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, L"TypeEquals", L"BWRegion"); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"ControlRegionStructureIndex", FORMAT_HEX_NOWIDTH, pTableBWRegion->ControlRegionStructureIndex); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NumberOfBlockDataWindows", FORMAT_HEX_NOWIDTH, pTableBWRegion->NumberOfBlockDataWindows); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"BlockDataWindowStartLogicalOffset", L"0x%lx", pTableBWRegion->BlockDataWindowStartLogicalOffset); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SizeOfBlockDataWindow", L"0x%lx", pTableBWRegion->SizeOfBlockDataWindow); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"AccessibleBlockCapacity", L"0x%lx", pTableBWRegion->AccessibleBlockCapacity); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"AccessibleBlockCapacityStartAddress", L"0x%lx\n", pTableBWRegion->AccessibleBlockCapacityStartAddress); break; case NVDIMM_FLUSH_HINT_TYPE: pTableFlushHint = (FlushHintTbl *)pTable; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, L"TypeEquals", L"FlushHint"); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NfitDeviceHandle", FORMAT_HEX, pTableFlushHint->DeviceHandle.AsUint32); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NfitDeviceHandle.DimmNumber", FORMAT_HEX_NOWIDTH, pTableFlushHint->DeviceHandle.NfitDeviceHandle.DimmNumber); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NfitDeviceHandle.MemChannel", FORMAT_HEX_NOWIDTH, pTableFlushHint->DeviceHandle.NfitDeviceHandle.MemChannel); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NfitDeviceHandle.MemControllerId", FORMAT_HEX_NOWIDTH, pTableFlushHint->DeviceHandle.NfitDeviceHandle.MemControllerId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NfitDeviceHandle.SocketId", FORMAT_HEX_NOWIDTH, pTableFlushHint->DeviceHandle.NfitDeviceHandle.SocketId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NfitDeviceHandle.NodeControllerId", FORMAT_HEX_NOWIDTH, pTableFlushHint->DeviceHandle.NfitDeviceHandle.NodeControllerId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NumberOfFlushHintAddresses", FORMAT_HEX_NOWIDTH, pTableFlushHint->NumberOfFlushHintAddresses); for (Index = 0; Index < pTableFlushHint->NumberOfFlushHintAddresses; Index++) { FlushHintAddress = CatSPrint(NULL, L"FlushHintAddress %d", Index); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, FlushHintAddress, FORMAT_UINT64_HEX_NOWIDTH, pTableFlushHint->FlushHintAddress[Index]); FREE_POOL_SAFE(FlushHintAddress); } break; case NVDIMM_PLATFORM_CAPABILITIES_TYPE: pTablePlatCap = (PlatformCapabilitiesTbl *)pTable; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, L"TypeEquals", L"PlatformCapabilities"); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"HighestValidCapability", L"0x%2.2x", pTablePlatCap->HighestValidCapability); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Capabilities", L"0x%8.8x", pTablePlatCap->Capabilities); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Capabilities.CPUCacheFlushToNVDIMM", FORMAT_INT32, (pTablePlatCap->Capabilities & CAPABILITY_CACHE_FLUSH) ? 1 : 0); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Capabilities.MemoryControllerFlushToNVDIMM", FORMAT_INT32, (pTablePlatCap->Capabilities & CAPABILITY_MEMORY_FLUSH) ? 1 : 0); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Capabilities.MemoryMirroring", FORMAT_INT32 L"\n", (pTablePlatCap->Capabilities & CAPABILITY_MEMORY_MIRROR) ? 1 : 0); break; default: break; } } /** PrintNFit - prints the header and all of the tables in the parsed NFit table. @param[in] pHeader pointer to the parsed NFit header. @param[in] pointer to command's printer context. **/ VOID PrintNFit( IN ParsedFitHeader *pHeader, IN PRINT_CONTEXT *pPrinterCtx ) { UINT16 Index = 0; if (pHeader == NULL) { return; } PRINTER_BUILD_KEY_PATH(pPath, DS_ACPI_INDEX_PATH, AcpiIndex); AcpiIndex++; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SYSTEM_TARGET_STR, L"NVDIMM Firmware Interface Table"); PrintAcpiHeader(&pHeader->pFit->Header, pPrinterCtx); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"BwRegionTablesNum", FORMAT_UINT32, pHeader->BWRegionTblesNum); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"ControlRegionTablesNum", FORMAT_UINT32, pHeader->ControlRegionTblesNum); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"FlushHintTablesNum", FORMAT_UINT32, pHeader->FlushHintTblesNum); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"InterleaveTablesNum", FORMAT_UINT32, pHeader->InterleaveTblesNum); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"NVDIMMRegionTablesNum", FORMAT_UINT32, pHeader->NvDimmRegionMappingStructuresNum); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"SmbiosTablesNum", FORMAT_UINT32, pHeader->SmbiosTblesNum); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"SpaRangeTablesNum", FORMAT_UINT32, pHeader->SpaRangeTblesNum); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"PlatformCapabilitiesTablesNum", FORMAT_UINT32 L"\n", pHeader->PlatformCapabilitiesTblesNum); for(Index = 0; Index < pHeader->BWRegionTblesNum; Index++) { PrintFitTable((SubTableHeader *)pHeader->ppBWRegionTbles[Index], pPrinterCtx); } for(Index = 0; Index < pHeader->ControlRegionTblesNum; Index++) { PrintFitTable((SubTableHeader *)pHeader->ppControlRegionTbles[Index], pPrinterCtx); } for(Index = 0; Index < pHeader->FlushHintTblesNum; Index++) { PrintFitTable((SubTableHeader *)pHeader->ppFlushHintTbles[Index], pPrinterCtx); } for(Index = 0; Index < pHeader->InterleaveTblesNum; Index++) { PrintFitTable((SubTableHeader *)pHeader->ppInterleaveTbles[Index], pPrinterCtx); } for(Index = 0; Index < pHeader->NvDimmRegionMappingStructuresNum; Index++) { PrintFitTable((SubTableHeader *)pHeader->ppNvDimmRegionMappingStructures[Index], pPrinterCtx); } for(Index = 0; Index < pHeader->SmbiosTblesNum; Index++) { PrintFitTable((SubTableHeader *)pHeader->ppSmbiosTbles[Index], pPrinterCtx); } for(Index = 0; Index < pHeader->SpaRangeTblesNum; Index++) { PrintFitTable((SubTableHeader *)pHeader->ppSpaRangeTbles[Index], pPrinterCtx); } for (Index = 0; Index < pHeader->PlatformCapabilitiesTblesNum; Index++) { PrintFitTable((SubTableHeader *)pHeader->ppPlatformCapabilitiesTbles[Index], pPrinterCtx); } FREE_POOL_SAFE(pPath); FREE_POOL_SAFE(pTypePath); } /** Prints the PMTT common header @param[in] pPmttCommonHeader pointer to the PMTT Common Header @param[in] pPrinterCtx pointer to command's printer context @param[in] Revision PMTT table revision **/ VOID PrintPmttCommonHeader( IN VOID *pPmttCommonHeader, IN PRINT_CONTEXT *pPrinterCtx, IN ACPI_REVISION Revision ) { PMTT_COMMON_HEADER *pCommonHeader = NULL; PMTT_COMMON_HEADER2 *pCommonHeader2 = NULL; if (pPmttCommonHeader == NULL || pPrinterCtx == NULL) { NVDIMM_DBG("NULL Pointer provided"); return; } if (!IS_ACPI_REV_MAJ_0_MIN_VALID(Revision)) { NVDIMM_DBG("Unknown PMTT Revision: ", FORMAT_UINT8_HEX, Revision.AsUint8); return; } pCommonHeader = (PMTT_COMMON_HEADER *)pPmttCommonHeader; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Type", FORMAT_UINT8, pCommonHeader->Type); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Reserved1", FORMAT_UINT8, pCommonHeader->Reserved1); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Length", FORMAT_UINT16, pCommonHeader->Length); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Flags", FORMAT_UINT16, pCommonHeader->Flags); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Reserved2", FORMAT_UINT16, pCommonHeader->Reserved2); if (IS_ACPI_REV_MAJ_0_MIN_2(Revision)) { pCommonHeader2 = (PMTT_COMMON_HEADER2 *)pPmttCommonHeader; PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NumOfMemoryDevices", FORMAT_UINT32, pCommonHeader2->NoOfMemoryDevices); } } /** Prints all of the sub-tables in the PMTT 0.1 table @param[in] pTable pointer to PMTT table @param[in] pPrinterCtx pointer to command's printer context **/ VOID PrintPmttRev1( IN VOID *pTable, IN PRINT_CONTEXT *pPrinterCtx ) { PMTT_TABLE *pPMTT = NULL; PMTT_COMMON_HEADER *pCommonHeader = NULL; UINT64 PmttLen = 0; UINT64 Offset = 0; if (pTable == NULL || pPrinterCtx == NULL) { NVDIMM_DBG("NULL Pointer provided"); return; } pPMTT = (PMTT_TABLE *)pTable; if (!IS_ACPI_REV_MAJ_0_MIN_1(pPMTT->Header.Revision)) { NVDIMM_DBG("Invalid PMTT Revision: ", FORMAT_HEX_NOWIDTH, pPMTT->Header.Revision.AsUint8); } PmttLen = pPMTT->Header.Length; Offset = sizeof(pPMTT->Header) + sizeof(pPMTT->Reserved); while (Offset < PmttLen) { pCommonHeader = (PMTT_COMMON_HEADER *)(((UINT8 *)pPMTT) + Offset); if (pCommonHeader->Type == PMTT_TYPE_SOCKET) { PMTT_SOCKET *pSocket = (PMTT_SOCKET *)(((UINT8 *)pPMTT) + Offset + PMTT_COMMON_HDR_LEN); PRINTER_BUILD_KEY_PATH(pTypePath, DS_ACPITYPE_INDEX_PATH, AcpiIndex - 1, TypeIndex); TypeIndex++; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, ACPI_TYPE_STR, L"Socket"); PrintPmttCommonHeader((VOID *)pCommonHeader, pPrinterCtx, pPMTT->Header.Revision); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SocketId", FORMAT_UINT16, pSocket->SocketId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Reserved3", FORMAT_UINT16, pSocket->Reserved3); Offset += sizeof(PMTT_SOCKET) + PMTT_COMMON_HDR_LEN; } else if (pCommonHeader->Type == PMTT_TYPE_iMC) { PMTT_iMC *piMC = (PMTT_iMC *)(((UINT8 *)pPMTT) + Offset + PMTT_COMMON_HDR_LEN); PRINTER_BUILD_KEY_PATH(pTypePath, DS_ACPITYPE_INDEX_PATH, AcpiIndex - 1, TypeIndex); TypeIndex++; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, ACPI_TYPE_STR, L"iMC"); PrintPmttCommonHeader((VOID *)pCommonHeader, pPrinterCtx, pPMTT->Header.Revision); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"ReadLatency", FORMAT_UINT32, piMC->ReadLatency); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"WriteLatency", FORMAT_UINT32, piMC->WriteLatency); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"ReadBW", FORMAT_UINT32, piMC->ReadBW); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"WriteBW", FORMAT_UINT32, piMC->WriteBW); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"OptimalAccessUnit", FORMAT_UINT16, piMC->OptimalAccessUnit); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"OptimalAccessAlignment", FORMAT_UINT16, piMC->OptimalAccessAlignment); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Reserved3", FORMAT_UINT16, piMC->Reserved3); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"NoOfProximityDomains", FORMAT_UINT16, piMC->NoOfProximityDomains); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"ProximityDomainArray", FORMAT_UINT32, piMC->ProximityDomainArray); Offset += sizeof(PMTT_iMC) + PMTT_COMMON_HDR_LEN; } else if (pCommonHeader->Type == PMTT_TYPE_MODULE) { PMTT_MODULE *pModule = (PMTT_MODULE *)(((UINT8 *)pPMTT) + Offset + PMTT_COMMON_HDR_LEN); PRINTER_BUILD_KEY_PATH(pTypePath, DS_ACPITYPE_INDEX_PATH, AcpiIndex - 1, TypeIndex); TypeIndex++; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, ACPI_TYPE_STR, L"MODULE"); PrintPmttCommonHeader((VOID *)pCommonHeader, pPrinterCtx, pPMTT->Header.Revision); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"PhysicalComponentId", FORMAT_UINT16, pModule->PhysicalComponentId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Reserved3", FORMAT_UINT16, pModule->Reserved3); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SizeOfDimm", FORMAT_UINT32, pModule->SizeOfDimm); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SmbiosHandle", FORMAT_HEX_PREFIX FORMAT_UINT32_HEX, pModule->SmbiosHandle); Offset += sizeof(PMTT_MODULE) + PMTT_COMMON_HDR_LEN; } } } /** Prints all of the sub-tables in PMTT 0.2 table @param[in] pTable pointer to PMTT table @param[in] pPrinterCtx pointer to command's printer context **/ VOID PrintPmttRev2( IN VOID *pTable, IN PRINT_CONTEXT *pPrinterCtx ) { PMTT_TABLE2 *pPMTT = NULL; PMTT_COMMON_HEADER2 *pCommonHeader = NULL; CHAR16 *pGuidStr = NULL; UINT64 PmttLen = 0; UINT64 Offset = 0; if (pTable == NULL || pPrinterCtx == NULL) { NVDIMM_DBG("NULL Pointer provided"); return; } pPMTT = (PMTT_TABLE2 *)pTable; if (!IS_ACPI_REV_MAJ_0_MIN_2(pPMTT->Header.Revision)) { NVDIMM_DBG("Invalid PMTT Revision: ", FORMAT_HEX_NOWIDTH, pPMTT->Header.Revision.AsUint8); } PmttLen = pPMTT->Header.Length; Offset = sizeof(pPMTT->Header) + sizeof(pPMTT->NoOfMemoryDevices); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pPath, L"NumOfMemoryDevices", FORMAT_UINT32, pPMTT->NoOfMemoryDevices); while (Offset < PmttLen) { pCommonHeader = (PMTT_COMMON_HEADER2 *)(((UINT8 *)pPMTT) + Offset); NVDIMM_DBG("Common table length: %d, mem devices: %d, Type: %d", pCommonHeader->Length, pCommonHeader->NoOfMemoryDevices, pCommonHeader->Type); if (pCommonHeader->Type == PMTT_TYPE_SOCKET) { PMTT_SOCKET2 *pSocket = (PMTT_SOCKET2 *)(((UINT8 *)pPMTT) + Offset); PRINTER_BUILD_KEY_PATH(pTypePath, DS_ACPITYPE_INDEX_PATH, AcpiIndex - 1, TypeIndex); TypeIndex++; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, ACPI_TYPE_STR, L"Socket"); PrintPmttCommonHeader((VOID *)pCommonHeader, pPrinterCtx, pPMTT->Header.Revision); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SocketId", FORMAT_UINT16, pSocket->SocketId); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Reserved3", FORMAT_UINT16, pSocket->Reserved3); Offset += sizeof(PMTT_SOCKET2); } else if (pCommonHeader->Type == PMTT_TYPE_iMC) { PMTT_iMC2 *piMC = (PMTT_iMC2 *)(((UINT8 *)pPMTT) + Offset); PRINTER_BUILD_KEY_PATH(pTypePath, DS_ACPITYPE_INDEX_PATH, AcpiIndex - 1, TypeIndex); TypeIndex++; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, ACPI_TYPE_STR, L"iMC"); PrintPmttCommonHeader((VOID *)pCommonHeader, pPrinterCtx, pPMTT->Header.Revision); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"MemControllerId", FORMAT_UINT16, piMC->MemControllerID); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Reserved3", FORMAT_UINT16, piMC->Reserved3); Offset += sizeof(PMTT_iMC2); } else if (pCommonHeader->Type == PMTT_TYPE_VENDOR_SPECIFIC) { PMTT_VENDOR_SPECIFIC2 *pVendorDevice = (PMTT_VENDOR_SPECIFIC2 *)(((UINT8 *)pPMTT) + Offset); PRINTER_BUILD_KEY_PATH(pTypePath, DS_ACPITYPE_INDEX_PATH, AcpiIndex - 1, TypeIndex); TypeIndex++; if (CompareGuid(&pVendorDevice->TypeUUID, &gDieTypeGuid)) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, ACPI_TYPE_STR, L"Die"); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"DieId", FORMAT_UINT16, pVendorDevice->DeviceID); } else if (CompareGuid(&pVendorDevice->TypeUUID, &gChannelTypeGuid)) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, ACPI_TYPE_STR, L"Channel"); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"ChannelId", FORMAT_UINT16, pVendorDevice->DeviceID); } else if (CompareGuid(&pVendorDevice->TypeUUID, &gSlotTypeGuid)) { PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, ACPI_TYPE_STR, L"Slot"); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SlotId", FORMAT_UINT16, pVendorDevice->DeviceID); } PrintPmttCommonHeader((VOID *)pCommonHeader, pPrinterCtx, pPMTT->Header.Revision); pGuidStr = GuidToStr(&pVendorDevice->TypeUUID); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"TypeUUID", FORMAT_STR, pGuidStr); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"Reserved3", FORMAT_UINT16, pVendorDevice->Reserved3); FREE_POOL_SAFE(pGuidStr); Offset += sizeof(PMTT_VENDOR_SPECIFIC2); } else if (pCommonHeader->Type == PMTT_TYPE_MODULE) { PMTT_MODULE2 *pModule = (PMTT_MODULE2 *)(((UINT8 *)pPMTT) + Offset); PRINTER_BUILD_KEY_PATH(pTypePath, DS_ACPITYPE_INDEX_PATH, AcpiIndex - 1, TypeIndex); TypeIndex++; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pTypePath, ACPI_TYPE_STR, L"MODULE"); PrintPmttCommonHeader((VOID *)pCommonHeader, pPrinterCtx, pPMTT->Header.Revision); PRINTER_SET_KEY_VAL_WIDE_STR_FORMAT(pPrinterCtx, pTypePath, L"SmbiosHandle", FORMAT_HEX_PREFIX FORMAT_UINT32_HEX, pModule->SmbiosHandle); Offset += sizeof(PMTT_MODULE2); } } } /** PrintPmtt - prints the header and all of the sub-tables in PMTT table. @param[in] pTable pointer to the PMTT table @param[in] pointer to command's printer context **/ VOID PrintPmtt( IN TABLE_HEADER *pTable, IN PRINT_CONTEXT *pPrinterCtx ) { if (pTable == NULL || pPrinterCtx == NULL) { NVDIMM_DBG("NULL Pointer provided"); return; } if (!IS_ACPI_REV_MAJ_0_MIN_VALID(pTable->Revision)) { NVDIMM_DBG("Unknown PMTT Revision: ", FORMAT_HEX_NOWIDTH, pTable->Revision.AsUint8); return; } PRINTER_BUILD_KEY_PATH(pPath, DS_ACPI_INDEX_PATH, AcpiIndex); AcpiIndex++; PRINTER_SET_KEY_VAL_WIDE_STR(pPrinterCtx, pPath, SYSTEM_TARGET_STR, L"Platform Memory Topology Table"); PrintAcpiHeader(pTable, pPrinterCtx); if (IS_ACPI_REV_MAJ_0_MIN_1(pTable->Revision)) { PrintPmttRev1((VOID *)pTable, pPrinterCtx); } else if (IS_ACPI_REV_MAJ_0_MIN_2(pTable->Revision)) { PrintPmttRev2((VOID *)pTable, pPrinterCtx); } FREE_POOL_SAFE(pPath); FREE_POOL_SAFE(pTypePath); } ipmctl-03.00.00.0485/DcpmPkg/common/ShowAcpi.h000066400000000000000000000053671440615110200203310ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SHOW_ACPI_H_ #define _SHOW_ACPI_H_ #include #define SYSTEM_TARGET_STR L"Target" #define ACPI_TYPE_STR L"TableType" #define DS_ROOT_PATH L"/AcpiList" #define DS_ACPI_PATH L"/AcpiList/Acpi" #define DS_ACPI_INDEX_PATH L"/AcpiList/Acpi[%d]" #define DS_ACPITYPE_PATH L"/AcpiList/Acpi/Type" #define DS_ACPITYPE_INDEX_PATH L"/AcpiList/Acpi[%d]/Type[%d]" #define NVDIMM_STATE_FLAGS_SAVE BIT0 #define NVDIMM_STATE_FLAGS_RESTORE BIT1 #define NVDIMM_STATE_FLAGS_FLUSH BIT2 #define NVDIMM_STATE_FLAGS_REGION_ARMED BIT3 #define NVDIMM_STATE_FLAGS_EVENTS_OBSERVED BIT4 #define NVDIMM_STATE_FLAGS_EVENTS_NOTIFY BIT5 #define NVDIMM_STATE_FLAGS_NOT_MAPPED BIT6 #define BIOS_SUPPORTS_CHANGING_CONFIG BIT0 #define BIOS_SUPPORTS_RUNTIME_INTERFACE BIT1 #define PERSISTENT_MEMORY_REGION_MIRRORING BIT0 #define PERSISTENT_MEMORY_REGION_SPARE BIT1 #define PERSISTENT_MEMORY_REGION_MIGRATION BIT2 /** PrintPcatHeader - prints the header of the parsed NFit table. @param[in] pPcat pointer to the parsed PCAT header. @param[in] pointer to command's printer context. **/ VOID PrintAcpiHeader( IN TABLE_HEADER *pHeader, IN PRINT_CONTEXT *pPrinterCtx ); /** PrintPcatTable - prints the subtable of the parsed PCAT table. @param[in] pTable pointer to the PCAT subtable. @param[in] pointer to command's printer context. **/ VOID PrintPcatTable( IN PCAT_TABLE_HEADER *pTable, IN PRINT_CONTEXT *pPrinterCtx ); /** PrintPcat - prints the header and all of the tables in the parsed PCAT table. @param[in] pPcat pointer to the parsed PCAT. @param[in] pointer to command's printer context. **/ VOID PrintPcat( IN ParsedPcatHeader *pPcat, IN PRINT_CONTEXT *pPrinterCtx ); /** PrintFitTable - prints the subtable of the parsed NFit table. @param[in] pTable pointer to the NFit subtable. @param[in] pointer to command's printer context. **/ VOID PrintFitTable( IN SubTableHeader *pTable, IN PRINT_CONTEXT *pPrinterCtx ); /** PrintNFit - prints the header and all of the tables in the parsed NFit table. @param[in] pHeader pointer to the parsed NFit header. @param[in] pointer to command's printer context. **/ VOID PrintNFit( IN ParsedFitHeader *pHeader, IN PRINT_CONTEXT *pPrinterCtx ); /** PrintPmtt - prints the header and all of the sub-tables in PMTT table. @param[in] pTable pointer to the PMTT table @param[in] pointer to command's printer context **/ VOID PrintPmtt( IN TABLE_HEADER *pTable, IN PRINT_CONTEXT *pPrinterCtx ); #endif ipmctl-03.00.00.0485/DcpmPkg/common/Strings.c000066400000000000000000000247001440615110200202300ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "Strings.h" CHAR8* string_concat(CHAR8* str1, CHAR8* str2, BOOLEAN free_when_done) { CHAR8* retval = NULL; if (!str1 || ! str2) { Print(L"NULL string passed as input to string_concat\n"); return NULL; } UINT64 len1 = string_length(str1); UINT64 len2 = string_length(str2); UINT64 size = (len1 + len2); retval = get_empty_string(size); MyMemCopy(retval, len1, str1); retval += len1; MyMemCopy(retval, len2, str2); retval -= len1; if (free_when_done) { FREE_POOL_SAFE(str1); FREE_POOL_SAFE(str2); } return retval; } CHAR8* string_array_concat(CHAR8** str_array, UINT64 string_count, BOOLEAN free_when_done, UINT64* final_length) { CHAR8* retval = NULL; CHAR8* head = NULL; UINT64 x = 0; UINT64 strlen = 0; *final_length = 0; for (x = 0; x < string_count; x++) { *final_length += string_length(str_array[x]); } retval = get_empty_string(*final_length); head = retval; for (x = 0; x < string_count; x++) { strlen = string_length(str_array[x]); MyMemCopy(head, strlen, str_array[x]); head += strlen; } if (free_when_done && NULL != str_array) { for (x = 0; x < string_count; x++) { if (NULL == str_array[x]) continue; FREE_POOL_SAFE(str_array[x]); } FREE_POOL_SAFE(str_array); } return retval; } UINT64 string_length(CHAR8* str) { UINT64 len = 0; if (!str) { return len; } while (str[len]) { len++; } return len; } CHAR8** string_split(CHAR8* str, CHAR8 splitchar, UINT64 maxelements, UINT64* elements) { CHAR8** retval = NULL; if (!str) { *elements = 0; return retval; } CHAR8** str_heads = NULL; UINT64* string_lengths = NULL; CHAR8* str_head = str; UINT64 len = 0; UINT32 x = 0; *elements = 1; //count the number of substrings, up to maxelements (unless it is <= 0) while (*str_head) { if (*str_head == splitchar) { *elements = *elements + 1; if (maxelements > 1 && *elements >= maxelements) { break; } } str_head++; } //allocate room for the return strings and metadata retval = AllocateZeroPool(*elements * sizeof(CHAR8*)); if (NULL == retval) { Print(L"Unable to allocate memory for new string\n"); goto Finish; } str_heads = AllocateZeroPool(*elements * sizeof(CHAR8*)); if (NULL == str_heads) { Print(L"Unable to allocate memory for new string\n"); goto Finish; } string_lengths = AllocateZeroPool(*elements * sizeof(UINT32*)); if (NULL == string_lengths) { Print(L"Unable to allocate memory for new string\n"); goto Finish; } for (; x < *elements; x++) { retval[x] = NULL; str_heads[x] = NULL; string_lengths[x] = 0; } x = 0; str_head = str; str_heads[x] = str_head; while (*str_head) { if (*str_head == splitchar) { x++; str_head++; //skip the split char str_heads[x] = str_head; if (x == ((*elements) - 1)) //Get the length of the rest of the string { while(*str_head) { string_lengths[x]++; str_head++; } break; } continue; } string_lengths[x]++; str_head++; } x = 0; for (; x < *elements; x++) { str_head = str_heads[x]; len = string_lengths[x]; if (len == 0) { retval[x] = NULL; continue; } retval[x] = get_empty_string(len); MyMemCopy(retval[x], len, str_head); } Finish: if (NULL != str_heads) { FREE_POOL_SAFE(str_heads); } if (NULL != string_lengths) { FREE_POOL_SAFE(string_lengths); } return retval; } UINT32 a_to_u32(CHAR8* str) { UINT32 retval = 0; if (!str) { return retval; } UINT32 x = 0; while (str[x]) { retval = retval * 10; UINT8 c = 0x7f & str[x]; retval += (c - 48); x++; } return retval; } UINT32 bytes_to_u32(UINT8* bytes) { UINT32 retval = 0; if (!bytes) { return retval; } UINT32 x0 = ((UINT32)bytes[0]); UINT32 x1 = ((UINT32)bytes[1]) << 8; UINT32 x2 = ((UINT32)bytes[2]) << 16; UINT32 x3 = ((UINT32)bytes[3]) << 24; retval = (UINT32)x0 + (UINT32)x1 + (UINT32)x2 + (UINT32)x3; return retval; } CHAR8* string_copy(CHAR8* source) { CHAR8* retval = NULL; if (!source) { return retval; } UINT64 len = 0; if (source) { len = string_length(source); } retval = get_empty_string(len); if (source) { MyMemCopy(retval, len, source); } return retval; } CHAR8* get_empty_string(UINT64 length) { UINT64 bytes = (length * sizeof(CHAR8)) + sizeof(CHAR8); CHAR8* retval = AllocateZeroPool(bytes); if (NULL == retval) { Print(L"Unable to allocate memory for new string\n"); return NULL; } return retval; } CHAR8* nlog_format( CHAR8 *format, UINT32** values, UINT64 values_length ) { UINT64 max_format_code_len = 10; UINT64 str_len = string_length(format); UINT64 format_len = 0; UINT64 current_value_index = 0; UINT64 chars_per_int = 15; UINT32 current_value = 0; if (NULL == values || !values_length) { return string_copy(format); } str_len = str_len + (values_length * chars_per_int); CHAR8* retval = get_empty_string(str_len); CHAR8* buffer = get_empty_string(max_format_code_len); //to hold format modifiers CHAR8* zero_buffer = get_empty_string(max_format_code_len); CHAR8* value_buffer = NULL; CHAR8* format_head = format; CHAR8* retval_head = retval; CHAR8* format_start; format_start = format_head; str_len = 0; while (*format_head) { //advance the format head until a format specifier is found if (*format_head != '%') { str_len++; format_head++; continue; } //found a format specifier now copy everything preceding it into a return buffer if (str_len) { MyMemCopy(retval_head, str_len, format_start); retval_head += str_len; format_start += str_len; } //can happen if no chars between format specifiers, i.e. %02x%04 if (*format_head == '%') { format_head++; format_start = format_head; } //clean out the format buffer MyMemCopy(buffer, max_format_code_len, zero_buffer); str_len = 0; while (*format_head && *format_head != 'X' && *format_head != 'x'&& *format_head != 'd' && *format_head != 'u') { format_head++; str_len++; } //figure out any length modifiers between the % and the X/x/d/u format_len = 0; if (str_len) { if (str_len < max_format_code_len) { MyMemCopy(buffer, str_len, format_start); if (string_length(buffer) > 0) { format_len = a_to_u32(buffer); } } else { //something bad... malformed request. Ignore. } } current_value = *values[current_value_index]; current_value_index++; if (*format_head == 'X') { value_buffer = u32_to_a(current_value, TRUE, format_len, TRUE); } else if (*format_head == 'x') { value_buffer = u32_to_a(current_value, TRUE, format_len, FALSE); } else if (*format_head == 'd') { value_buffer = u32_to_a(current_value, FALSE, format_len, FALSE); } else if (*format_head == 'u') { value_buffer = u32_to_a(current_value, FALSE, format_len, FALSE); } if (format_len > 0) { str_len = string_length(value_buffer); if (format_len > 0 && str_len < format_len) { value_buffer = pad_left(value_buffer, format_len, '0', TRUE); } } str_len = string_length(value_buffer); MyMemCopy(retval_head, str_len, value_buffer); retval_head += str_len; FREE_POOL_SAFE(value_buffer); format_head++; //consume the format char format_start = format_head; str_len = 0; } if (str_len && format_start) { MyMemCopy(retval_head, str_len, format_start); } FREE_POOL_SAFE(buffer); FREE_POOL_SAFE(zero_buffer); return retval; } CHAR8* u32_to_a( UINT32 val, BOOLEAN format_hex, UINT64 max_len, BOOLEAN uppercase ) { if (val == 0) { return string_copy("0"); } CHAR8* volatile retval = NULL; CHAR8* volatile int_str = NULL; CHAR8* volatile int_str_head = NULL; CHAR8* volatile int_str_ptr = NULL; CHAR8* new_retval = NULL; UINT64 trim = 0; UINT32 base = 10; UINT64 len = 0; UINT32 cntr = 0; UINT32 current_val; if (format_hex) { base = 16; } if (16 == base) { len = 8; } else if (10 == base) { len = 10; } int_str = get_empty_string(len); int_str_ptr = int_str + (len - 1); cntr = 0; int_str_head = int_str; while (cntr < len) { cntr++; *int_str_head = '0'; int_str_head++; } while (val > 0) { current_val = (val % base); len++; if (current_val <= 9) { *int_str_ptr = (CHAR8)(current_val + (UINT32)'0'); } else { if (uppercase) { *int_str_ptr = (CHAR8)((current_val - 10) + (UINT32)'A'); } else { *int_str_ptr = (CHAR8)((current_val - 10) + (UINT32)'a'); } } int_str_ptr--; val = val / base; } int_str_ptr++; len--; retval = get_empty_string(len); MyMemCopy(retval, len, int_str_ptr); FREE_POOL_SAFE(int_str); if (0 < max_len) { len = string_length(retval); if (len > max_len) { trim = len - max_len; new_retval = get_empty_string(max_len); MyMemCopy(new_retval, max_len, (retval + trim)); FREE_POOL_SAFE(retval); retval = new_retval; } else if (len < max_len) { retval = pad_left(retval, max_len, '0', TRUE); } } return retval; } CHAR8* pad_left( CHAR8 *str, UINT64 pad_len, CHAR8 pad_char, BOOLEAN free_when_done ) { CHAR8* volatile retval = NULL; UINT64 x = 0; if (NULL == str) { return retval; } UINT64 current_length = string_length(str); if (current_length >= pad_len) { retval = string_copy(str); } else { pad_len = pad_len - current_length; retval = get_empty_string(pad_len + current_length); x = 0; for (; x < pad_len; x++) { retval[x] = pad_char; } MyMemCopy((retval + pad_len), current_length, str); } if (free_when_done) { FREE_POOL_SAFE(str); } return retval; } CHAR8* MyMemCopy( CHAR8* dest, UINT64 len, CHAR8* source ) { CHAR8* volatile s = source; CHAR8* volatile d = dest; if (d && s) { UINT64 x = 0; for (; x < len; x++) { d[x] = s[x]; } } return d; } ipmctl-03.00.00.0485/DcpmPkg/common/Strings.h000066400000000000000000000072201440615110200202330ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _STRINGS_H_ #define _STRINGS_H_ #include #include "Common.h" /** string_concat command @param[in] str1 - string 1 @param[in] str2 - string 2 @param[in] free_when_done - if it should release the memory of the strings when done @retval the concatenated string **/ CHAR8* string_concat( IN CHAR8* str1, IN CHAR8* str2, IN BOOLEAN free_when_done ); /** string concatenate command - THIS FREES THE PASSED STRINGS WHEN FINISHED @param[in] str_array - string array @param[in] string_count - the length of the string array @param[in] free_when_done - if it should release the memory of the strings when done @param[out] final_length - if it should release the memory of the strings when done @retval the concatenated string **/ CHAR8* string_array_concat( IN CHAR8** str_array, IN UINT64 string_count, IN BOOLEAN free_when_done, OUT UINT64* final_length ); /* string copy command @param[in] source - the source string @retval a copy of the string */ CHAR8* string_copy( IN CHAR8* source ); /* get_empty_string command @param[in] length - how long of a string that you want @retval a string [length] long + terminator, initialized with 0 in each index */ CHAR8* get_empty_string( IN UINT64 length ); /* string_length command @param[in] str - the string to measure @retval the length of the passed string */ UINT64 string_length( IN CHAR8* str ); /* string_split command @param[in] str - the string to split @param[in] splitchar - the char to split on @param[in] maxelements - 0 for unlimited, or > 0 to prevent too many resulting strings @param[out] elements - the number of found elements @retval an array of strings [elements] long */ CHAR8** string_split( IN CHAR8* str, IN CHAR8 splitchar, IN UINT64 maxelements, OUT UINT64* elements ); /* bytes_to_u32 command @param[in] bytes - the bytes to convert (must be 4 or more) @retval the UINT32 value of the passed string */ UINT32 bytes_to_u32( IN UINT8* bytes ); /* a_to_u32 command @param[in] str - the string to convert @retval the UINT32 value of the passed string */ UINT32 a_to_u32( IN CHAR8* str ); /* u32_to_a command @param[in] val - the value to convert @param[in] format_hex - if the output should be hex @param[in] max_len - the max length of string to return @param[in] uppercase - if upper case values should be used when hex @retval the string representation of the passed value */ CHAR8* u32_to_a( IN UINT32 val, IN BOOLEAN format_hex, IN UINT64 max_len, IN BOOLEAN uppercase ); /* nlog_format command @param[in] format - the string to base a format on (processes %X, %x and %d only) @param[in] values - the values to add to the format str @param[in] values_length - the length of the values array @retval returns the formatted string */ CHAR8* nlog_format( IN CHAR8 *format, IN UINT32** values, IN UINT64 values_length ); /* Pads a string AND FREES THE PASSED ONE @param[in] str - the string to pad @param[in] pad_len - the length the final string should be @param[in] pad_char - the char to use for padding @param[in] free_when_done - if it should release the memory of the strings when done @retval returns the padded string */ CHAR8* pad_left( IN CHAR8 *str, IN UINT64 pad_len, IN CHAR8 pad_char, IN BOOLEAN free_when_done ); /* Copys one memory range's value to another @param[in] dest - the string to pad @param[in] len - the char to use for padding @param[in] source - if it should release the memory of the strings when done @retval returns a reference to the dest */ CHAR8* MyMemCopy( IN CHAR8* dest, IN UINT64 len, IN CHAR8* source ); #endif ipmctl-03.00.00.0485/DcpmPkg/common/Types.h000066400000000000000000000243621440615110200177140ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file Types.h * @brief Types for EFI_DCPMM_CONFIG2_PROTOCOL to configure and manage PMem modules. These types do not compile with VFR compiler and are kept separate. */ #ifndef _TYPES_H_ #define _TYPES_H_ #include "NvmTypes.h" #include #include #ifdef __GNUC__ // Need this to build for UEFI/Linux, as size_t is not found by default. #include #endif #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) #define PMEM_MODULE_NAME L"Intel(R) PMem module " //short version for drivers list #define PMEM_MODULE_NAME_SEARCH L"Intel(R),PMemModule" //comma separated search string #define PMEM_DIMM_NAME L"Intel Persistent Memory Module %d Controller" /*This should match the error_type definition in nvm_management.h*/ #define ERROR_INJ_POISON 0X01 #define ERROR_INJ_TEMPERATURE 0X02 #define ERROR_INJ_PACKAGE_SPARING 0X03 #define ERROR_INJ_PERCENTAGE_REMAINING 0X04 #define ERROR_INJ_FATAL_MEDIA_ERR 0X05 #define ERROR_INJ_DIRTY_SHUTDOWN 0X06 #define ERROR_INJ_TYPE_INVALID 0x08 /** The device path type for our driver and HII driver. **/ typedef struct { VENDOR_DEVICE_PATH VendorDevicePath; EFI_DEVICE_PATH_PROTOCOL End; } VENDOR_END_DEVICE_PATH; #define PMEMDEV_INITIALIZER(DEV) \ InitializeListHead(DEV.Dimms); \ InitializeListHead(DEV.ISs); \ InitializeListHead(DEV.ISsNfit); \ InitializeListHead(DEV.Namespaces); #define DIMM_BSR_MAJOR_NO_POST_CODE 0x0 #define DIMM_BSR_ROM_MAJOR_CHECKPOINT_INIT_FAILURE 0x91 // Unrecoverable error in ROM init #define DIMM_BSR_MAJOR_CHECKPOINT_INIT_FAILURE 0xA1 // Unrecoverable FW error #define DIMM_BSR_MAJOR_CHECKPOINT_CPU_EXCEPTION 0xE1 // CPU exception #define DIMM_BSR_MAJOR_CHECKPOINT_INIT_COMPLETE 0xF0 // FW initialization complete #define DIMM_BSR_MAILBOX_READY 0x1 #define DIMM_BSR_MAILBOX_NOT_READY 0x0 #define DIMM_BSR_MEDIA_NOT_TRAINED 0x0 #define DIMM_BSR_MEDIA_TRAINED 0x1 #define DIMM_BSR_MEDIA_ERROR 0x2 #define DIMM_BSR_MEDIA_DISABLED 0x1 #define DIMM_BSR_PCR_UNLOCKED 0x0 #define DIMM_BSR_PCR_LOCKED 0x1 #define DIMM_BSR_DDRT_IO_INIT_NOT_STARTED 0x0 // FIS >= 1.5 #define DIMM_BSR_AIT_DRAM_NOT_TRAINED 0x0 #define DIMM_BSR_AIT_DRAM_TRAINED_NOT_LOADED 0x1 #define DIMM_BSR_AIT_DRAM_ERROR 0x2 #define DIMM_BSR_AIT_DRAM_TRAINED_LOADED_READY 0x3 #define DIMM_BSR_SVNDE_ENABLED 0x1 #define DIMM_BSR_REBOOT_REQUIRED 0x1 #define DIMM_BSR_MEDIA_INTERFACE_ENGINE_STALLED 0x01 #define REGISTER_BSR_STR L"BSR" #define REGISTER_OS_STR L"OS" typedef union { UINT64 AsUint64; struct { UINT64 Major : 8; //7:0 UINT64 Minor : 8; //15:8 UINT64 MR : 2; //17:16 UINT64 DT : 1; //18 UINT64 PCR : 1; //19 UINT64 MBR : 1; //20 UINT64 WTS : 1; //21 UINT64 FRCF : 1; //22 UINT64 CR : 1; //23 UINT64 MD : 1; //24 UINT64 SVNDE : 1; //25 UINT64 SVNCOIS : 1; //26 UINT64 DR : 2; //28:27 UINT64 RR : 1; //29 UINT64 LFOPB : 1; //30 UINT64 SVNWC : 1; //31 UINT64 Rsvd : 2; //33:32 UINT64 DTS : 2; //35:34 UINT64 Rsvd1 : 28; //63:36 } Separated_FIS_1_15; // PMem module Gen 1 FIS struct { UINT64 Major : 8; //7:0 UINT64 Minor : 8; //15:8 UINT64 MR : 2; //17:16 UINT64 DT : 1; //18 UINT64 PCR : 1; //19 UINT64 MBR : 1; //20 UINT64 WTS : 1; //21 UINT64 FRCF : 1; //22 UINT64 CR : 1; //23 UINT64 MD : 1; //24 UINT64 SVNDE : 1; //25 UINT64 SVNCOIS : 1; //26 UINT64 DR : 2; //28:27 UINT64 RR : 1; //29 UINT64 LFOPB : 1; //30 UINT64 SVNWC : 1; //31 UINT64 Rsvd : 2; //33:32 UINT64 DTS : 2; //35:34 UINT64 FAC : 1; //36 UINT64 Rsvd1 : 27; //63:37 } Separated_Current_FIS; } DIMM_BSR; /** Contains SMART and Health attributes of a PMem module **/ typedef struct _SMART_AND_HEALTH_INFO { BOOLEAN PercentageRemainingValid; ///< Indicates if PercentageRemaining is valid BOOLEAN MediaTemperatureValid; ///< Indicates if MediaTemperature is valid BOOLEAN ControllerTemperatureValid; ///< Indicates if ControllerTemperature is valid BOOLEAN MediaTemperatureTrip; ///< Indicates if Media Temperature alarm threshold has tripped BOOLEAN ControllerTemperatureTrip; ///< Indicates if Controller Temperature alarm threshold has tripped BOOLEAN PercentageRemainingTrip; ///< Indicates if Percentage Remaining alarm threshold has tripped INT16 MediaTemperature; ///< Current Media Temperature in degrees Celsius INT16 ControllerTemperature; ///< Current Controller Temperature in degrees Celsius UINT8 PercentageRemaining; ///< Remaining module's life as a percentage value of factory expected life span (0-100) UINT32 LatchedDirtyShutdownCount; ///< Latched Dirty Shutdowns count UINT8 LatchedLastShutdownStatus; ///< Latched Last Shutdown Status. See FIS field LSS for additional details UINT8 UnlatchedLastShutdownStatus; UINT32 PowerOnTime; ///< Lifetime PMem module has been powered on in seconds. See FIS field POT for additional details UINT32 UpTime; ///< PMem module uptime in seconds since last AC cycle. See FIS field UT for additional details UINT64 PowerCycles; ///< Number of PMem module power cycles. See FIS field PC for additional details UINT8 HealthStatus; ///< Overall health summary as specified by @ref HEALTH_STATUS. See FIS field HS for additional details. UINT16 HealthStatusReason; ///< Indicates why the module is in the current HealthStatus as specified by @ref HEALTH_STATUS_REASONS. See FIS field HSR for additional details. UINT32 MediaErrorCount; ///< Total count of media errors found in Error Log UINT32 ThermalErrorCount; ///< Total count of thermal errors found in Error Log INT16 ContrTempShutdownThresh; ///< Controller temperature shutdown threshold in degrees Celsius INT16 MediaTempShutdownThresh; ///< Media temperature shutdown threshold in degrees Celsius INT16 MediaThrottlingStartThresh; ///< Media throttling start temperature threshold in degrees Celsius INT16 MediaThrottlingStopThresh; ///< Media throttling stop temperature threshold in degrees Celsius INT16 ControllerThrottlingStartThresh;///< Controller throttling stop temperature threshold in degrees Celsius INT16 ControllerThrottlingStopThresh; ///< Controller throttling stop temperature threshold in degrees Celsius UINT32 UnlatchedDirtyShutdownCount; ///< Unlatched Dirty Shutdowns count UINT32 LatchedLastShutdownStatusDetails; UINT32 UnlatchedLastShutdownStatusDetails; UINT64 LastShutdownTime; UINT8 AitDramEnabled; UINT8 ThermalThrottlePerformanceLossPrct; INT16 MaxMediaTemperature; //!< The highest die temperature reported in degrees Celsius. INT16 MaxControllerTemperature; //!< The highest controller temperature reported in degrees Celsius. } SMART_AND_HEALTH_INFO; /** Individual sensor attributes struct **/ typedef struct { UINT8 Type; UINT8 State; //!< DEPRECATED; current state of a given PMem module sensor UINT8 Enabled; INT64 Value; UINT8 SettableThresholds; UINT8 SupportedThresholds; INT64 AlarmThreshold; INT64 ThrottlingStopThreshold; INT64 ThrottlingStartThreshold; INT64 ShutdownThreshold; INT64 MaxTemperature; } DIMM_SENSOR; /** Thermal error log info struct **/ typedef struct _THERMAL_ERROR_LOG_PER_DIMM_INFO { INT16 Temperature; //!< In degrees Celsius UINT8 Reported; //!< Temperature being reported UINT8 Type; //!< Which device the temperature is for UINT16 SequenceNum; //!< Log entry sequence number UINT8 Reserved[1]; } THERMAL_ERROR_LOG_INFO; /** Media error log info struct **/ typedef struct _MEDIA_ERROR_LOG_PER_DIMM_INFO { UINT64 Dpa; //!< Specifies DPA address of error UINT64 Pda; //!< Specifies PDA address of the failure UINT8 Range; //!< Specifies the length in address space of this error. UINT8 ErrorType; //!< Indicates what kind of error was logged. See @ref ERROR_LOG_TYPES. UINT8 PdaValid; //!< Indicates the PDA address is valid. UINT8 DpaValid; //!< Indicates the DPA address is valid. UINT8 Interrupt; //!< Indicates this error generated an interrupt packet. UINT8 Viral; //!< Indicates Viral was signaled for this error. UINT8 TransactionType; //!< Transaction type. UINT16 SequenceNum; //!< Log entry sequence number. UINT8 Reserved[2]; } MEDIA_ERROR_LOG_INFO; /** Passthrough Output Command Effect Log Entry Format **/ typedef struct { union { struct { UINT32 Opcode : 8; UINT32 SubOpcode : 8; UINT32 : 16; } Separated; UINT32 AsUint32; } Opcode; union { struct { UINT32 NoEffects : 1; UINT32 SecurityStateChange : 1; UINT32 DimmConfigChangeAfterReboot : 1; UINT32 ImmediateDimmConfigChange : 1; UINT32 QuiesceAllIo : 1; UINT32 ImmediateDimmDataChange : 1; UINT32 TestMode : 1; UINT32 DebugMode : 1; UINT32 ImmediateDimmPolicyChange : 1; UINT32 : 23; } Separated; UINT32 AsUint32; } EffectName; } COMMAND_EFFECT_LOG_ENTRY; /// /// FIPS Mode Status /// typedef enum { FIPSModeStatusNonFIPSMode = 0x00, FIPSModeStatusNonFIPSModeUntilNextBoot = 0x01, FIPSModeStatusInitializationNotDone = 0x02, FIPSModeStatusInitializationDone = 0x03 } FIPS_MODE_STATUS; /** Get FIPS Mode struct **/ typedef struct { UINT8 Status; //!< FIPS mode status, see above UINT8 Reserved[127]; } FIPS_MODE; /** * @defgroup ERROR_LOG_TYPES Error Log Types * @{ */ #define THERMAL_ERROR 0 ///< Thermal error log type #define MEDIA_ERROR 1 ///< Media error log type /** @} */ #endif /** _TYPES_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/Utility.c000066400000000000000000003127371440615110200202540ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include "Utility.h" #include #include #include #include #include #include #ifdef OS_BUILD #include #include #include #endif #include "Version.h" #include "FwVersion.h" extern EFI_GUID gIntelDimmConfigVariableGuid; CHAR16 gFnName[1024]; #ifdef _MSC_VER int _fltused() { return 0; } #endif #define NOT_RFC4646_ABRV_LANGUAGE_LEN 3 /** Removes all whitespace from before, after, and inside a passed string @param[IN, OUT] buffer - The string to remove whitespace from **/ VOID RemoveAllWhiteSpace( CHAR16* buffer) { CHAR16* nextNonWs = NULL; if (buffer == NULL) { return; } TrimString(buffer); nextNonWs = buffer; while (*buffer) { //Advance the forward-pointer to the next non WS char while (*nextNonWs && *nextNonWs <= L' ') { nextNonWs++; } if (buffer != nextNonWs) { *buffer = *nextNonWs; if (0 == *nextNonWs) { break; } } buffer++; nextNonWs++; } } /** Generates namespace type string, caller must free it @param[in] Type, value corresponding to namespace type. @retval Pointer to type string **/ CHAR16* NamespaceTypeToString( IN UINT8 Type ) { CHAR16 *pTypeString = NULL; switch(Type) { case APPDIRECT_NAMESPACE: pTypeString = CatSPrint(NULL, FORMAT_STR, L"AppDirect"); break; default: pTypeString = CatSPrint(NULL, FORMAT_STR, L"Unknown"); break; } return pTypeString; } /** Generates string from diagnostic output to print and frees the diagnostic structure @param[in] Type, pointer to type structure @retval Pointer to type string **/ CHAR16 *DiagnosticResultToStr( IN DIAG_INFO *pResult ) { CHAR16 *pOutputLines = NULL; UINT32 NumTokens = 0; CHAR16 *MsgStr = NULL; UINT8 index = 0; UINT8 Id = 0; if (pResult->TestName != NULL) { pOutputLines = CatSPrintClean(pOutputLines, L"\n***** %ls = %ls *****\n", pResult->TestName, pResult->State); CHAR16 **TestEventMesg = StrSplit(pResult->Message, L'\n', &NumTokens); if (TestEventMesg != NULL) { pOutputLines = CatSPrintClean(pOutputLines, L"Message : %ls\n", TestEventMesg[0]); FreeStringArray(TestEventMesg, NumTokens); } } for (Id = 0; Id < MAX_NO_OF_DIAGNOSTIC_SUBTESTS; Id++) { if (pResult->SubTestName[Id] != NULL) { pOutputLines = CatSPrintClean(pOutputLines, L" %-20ls = %ls\n", pResult->SubTestName[Id], pResult->SubTestState[Id]); if (pResult->SubTestMessage[Id] != NULL) { CHAR16 **ppSplitSubTestMessage = StrSplit(pResult->SubTestMessage[Id], L'\n', &NumTokens); if (ppSplitSubTestMessage != NULL) { for (index = 0; index < NumTokens; index++) { MsgStr = CatSPrintClean(MsgStr, L"Message.%d", index + 1); pOutputLines = CatSPrintClean(pOutputLines, L" %ls = %ls\n", MsgStr, ppSplitSubTestMessage[index]); FREE_POOL_SAFE(MsgStr); } } FreeStringArray(ppSplitSubTestMessage, NumTokens); } FREE_POOL_SAFE(pResult->SubTestName[Id]); FREE_POOL_SAFE(pResult->SubTestMessage[Id]); FREE_POOL_SAFE(pResult->SubTestState[Id]); } } FREE_POOL_SAFE(pResult->TestName); FREE_POOL_SAFE(pResult->Message); FREE_POOL_SAFE(pResult->State); return pOutputLines; } /** Generates pointer to string with value corresponding to health state Caller is responsible for FreePool on this pointer **/ CHAR16* NamespaceHealthToString( IN UINT16 Health ) { CHAR16 *pHealthString = NULL; switch(Health) { case NAMESPACE_HEALTH_OK: pHealthString = CatSPrint(NULL, FORMAT_STR, HEALTHSTATE_OK); break; case NAMESPACE_HEALTH_WARNING: pHealthString = CatSPrint(NULL, FORMAT_STR, HEALTHSTATE_WARNING); break; case NAMESPACE_HEALTH_CRITICAL: pHealthString = CatSPrint(NULL, FORMAT_STR, HEALTHSTATE_CRITICAL); break; case NAMESPACE_HEALTH_UNSUPPORTED: pHealthString = CatSPrint(NULL, FORMAT_STR, HEALTHSTATE_UNSUPPORTED); break; case NAMESPACE_HEALTH_LOCKED: pHealthString = CatSPrint(NULL, FORMAT_STR, HEALTHSTATE_LOCKED); break; default: pHealthString = CatSPrint(NULL, FORMAT_STR, HEALTHSTATE_UNKNOWN); break; } return pHealthString; } /** Check if LIST_ENTRY list is initialized @param[in] ListHead list head @retval BOOLEAN list initialization status **/ BOOLEAN IsListInitialized( IN LIST_ENTRY ListHead ) { return !(ListHead.BackLink == NULL || ListHead.ForwardLink == NULL || ListHead.BackLink == BAD_POINTER || ListHead.ForwardLink == BAD_POINTER); } /** Calculate checksum using Fletcher64 algorithm and compares it at the given offset. The length parameter must be aligned to 4 (32bit). @param[in] pAddress Starting address of area to calculate checksum on @param[in] Length Length of area over which checksum is calculated @param[in, out] pChecksum, the pointer where the checksum lives in @param[in] Insert, flag telling if the checksum should be inserted at the specified address or just compared to it @retval TRUE if the compared checksums are equal @retval FALSE if the checksums differ or the input parameters are invalid (a NULL was passed or the length is not aligned) **/ BOOLEAN ChecksumOperations( IN VOID *pAddress, IN UINT64 Length, IN OUT UINT64 *pChecksum, IN BOOLEAN Insert ) { UINT32 *p32 = pAddress; UINT32 *p32End = (UINT32 *)((UINT8 *)pAddress + Length); UINT32 Lo32 = 0; UINT32 Hi32 = 0; UINT64 Checksum = 0; BOOLEAN ChecksumMatch = FALSE; if ((Length % sizeof(UINT32)) != 0) { NVDIMM_DBG("The size specified for the checksum is not properly aligned"); return FALSE; } if (((UINT64) pAddress % sizeof(UINT32)) != ((UINT64) pChecksum % sizeof(UINT32))) { NVDIMM_DBG("The address and the checksum address are not aligned together"); return FALSE; } if (pAddress == NULL || pChecksum == NULL) { NVDIMM_DBG("The address or checksum pointer equal NULL"); return FALSE; } while (p32 < p32End) { if (p32 == (UINT32 *) pChecksum) { /* Lo32 += 0; treat first 32-bits as zero */ p32++; Hi32 += Lo32; /* Lo32 += 0; treat second 32-bits as zero */ p32++; Hi32 += Lo32; } else { Lo32 += *p32; ++p32; Hi32 += Lo32; } } Checksum = (UINT64) Hi32 << 32 | Lo32; if (Insert) { *pChecksum = Checksum; return TRUE; } ChecksumMatch = (*pChecksum == Checksum); if (!ChecksumMatch) { NVDIMM_DBG("Checksum = %llx", *pChecksum); NVDIMM_DBG("Calculated checksum = %llx", Checksum); } return ChecksumMatch; } /** Compares the two provided 128bit unsigned ints. @param[in] LeftValue is the first 128bit uint. @param[in] RightValue is the second 128bit uint. @retval -1 when the LeftValue is smaller than the RightValue @retval 0 when the provided values are the same @retval 1 when the LeftValue is bigger than the RightValue **/ INT8 CompareUint128( IN UINT128 LeftValue, IN UINT128 RightValue ) { if (LeftValue.Uint64_1 > RightValue.Uint64_1) { return 1; } else if (LeftValue.Uint64_1 == RightValue.Uint64_1) { if (LeftValue.Uint64 > RightValue.Uint64) { return 1; } else if (LeftValue.Uint64 == RightValue.Uint64) { return 0; } else { return -1; } } else { return -1; } } /** Tokenize a string by the specified delimiter and update the input to the remainder. NOTE: Returned token needs to be freed by the caller **/ CHAR16 *StrTok(CHAR16 **input, CONST CHAR16 delim) { CHAR16 *token; UINT16 tokenLength; UINTN i; UINTN j; BOOLEAN found; found = FALSE; token = NULL; /** check input **/ if ((input != NULL) && (*input != NULL) && ((*input)[0] != 0)) { i = 0; while ((*input)[i] != 0) { /** found the delimiter **/ if ((*input)[i] == delim) { found = TRUE; /** create the token **/ token = AllocatePool((i + 1) * sizeof(CHAR16)); if (!token) { NVDIMM_DBG("StrTok failed due to lack of resources"); } else { /** copy the token **/ for (j = 0; j < i; j++) { token[j] = (*input)[j]; } token[j] = 0; /** null terminate **/ /** reset the input to the remainder **/ for (j = i; j < StrLen(*input); j++) { (*input)[j - i] = (*input)[j + 1]; } } break; } i++; } /** set the token to the end of the string and set the remainder to null **/ if (!found) { tokenLength = (UINT16)StrLen(*input) + 1; token = AllocatePool(tokenLength * sizeof(CHAR16)); if (!token) { NVDIMM_DBG("StrTok failed due to lack of resources"); } else { StrnCpyS(token, tokenLength, *input, tokenLength - 1); } /** set input to null **/ (*input)[0] = 0; } } return token; } /** Tokenize provided ASCII string @param[in] ppInput Input string @param[in] pDelimiter Delimiter character @retval Pointer to token string **/ CHAR8 *AsciiStrTok(CHAR8 **ppInput, CONST CHAR8 delim) { CHAR8 *pToken = NULL; UINT16 TokenLength = 0; UINTN Index = 0; UINTN Index2 = 0; BOOLEAN Found = FALSE; /** check input **/ if ((ppInput != NULL) && (*ppInput != NULL) && ((*ppInput)[0] != 0)) { Index = 0; while ((*ppInput)[Index] != 0) { /** found the delimiter **/ if ((*ppInput)[Index] == delim) { Found = TRUE; /** create the token **/ pToken = AllocatePool((Index + 1) * sizeof(CHAR8)); if (!pToken) { NVDIMM_DBG("StrTok failed due to lack of resources"); } else { /** copy the token **/ for (Index2 = 0; Index2 < Index; Index2++) { pToken[Index2] = (*ppInput)[Index2]; } pToken[Index2] = 0; /** null terminate **/ /** reset the input to the remainder **/ for (Index2 = Index; Index2 < AsciiStrLen(*ppInput); Index2++) { (*ppInput)[Index2 - Index] = (*ppInput)[Index2 + 1]; } } break; } Index++; } /** set the token to the end of the string and set the remainder to null **/ if (!Found) { TokenLength = (UINT16)AsciiStrLen(*ppInput) + 1; pToken = AllocatePool(TokenLength * sizeof(CHAR8)); if (!pToken) { NVDIMM_DBG("StrTok failed due to lack of resources"); } else { AsciiStrnCpyS(pToken, TokenLength, *ppInput, TokenLength - 1); } /** set input to null **/ (*ppInput)[0] = 0; } } return pToken; } /** Split a string by the specified delimiter and return the split string as a string array. The caller is responsible for a memory deallocation of the returned array and its elements. @param[in] pInput the input string to split @param[in] Delimiter delimiter to split the string @param[out] pArraySize array size will be put here @retval NULL at least one of parameters is NULL or memory allocation failure @retval the split input string as an array **/ CHAR16 ** StrSplit( IN CHAR16 *pInput, IN CHAR16 Delimiter, OUT UINT32 *pArraySize ) { CHAR16 **ppArray = NULL; CHAR16 *pInputTmp = NULL; UINT32 Index = 0; UINT32 DelimiterCounter = 0; CHAR16 *pBuff = NULL; if (pInput == NULL || pArraySize == NULL) { NVDIMM_DBG("At least one of parameters is NULL."); goto Finish; } if (pInput[0] == L'\0') { goto Finish; } /** Count the number of delimiter in the string **/ for (Index = 0; pInput[Index] != L'\0'; Index++) { if (pInput[Index] == Delimiter) { DelimiterCounter += 1; } } /** 1. "A,B,C": 2 delimiter, 3 array elements 2. "A,B,": 2 delimiter, 2 array elements - StrTok returns NULL if there is '\0' after the last delimiter instead of empty string **/ if (pInput[Index - 1] != Delimiter) { DelimiterCounter += 1; } *pArraySize = DelimiterCounter; /** Allocate an array memory and fill it with split input string **/ ppArray = AllocateZeroPool(*pArraySize * sizeof(CHAR16 *)); if (ppArray == NULL) { NVDIMM_ERR("Memory allocation failed."); goto Finish; } pInputTmp = AllocateZeroPool((StrLen(pInput) + 1) * sizeof(CHAR16)); /** Copy the input to a tmp var to avoid changing it **/ CopyMem(pInputTmp, pInput, (StrLen(pInput) * sizeof(CHAR16))); if (pInputTmp == NULL) { NVDIMM_ERR("Memory allocation failed."); goto FinishCleanMemory; } /*Need to hold the address of pInputTmp to safe free. */ pBuff = pInputTmp; for (Index = 0; Index < *pArraySize; Index++) { ppArray[Index] = StrTok(&pInputTmp, Delimiter); if (ppArray[Index] == NULL) { goto FinishCleanMemory; } } /** Success path **/ goto Finish; /** Error path **/ FinishCleanMemory: FreeStringArray(ppArray, *pArraySize); ppArray = NULL; *pArraySize = 0; Finish: FREE_POOL_SAFE(pBuff); return ppArray; } /** Split an ASCII string by the specified delimiter and return the split string as a string array. The caller is responsible for a memory deallocation of the returned array and its elements. @param[in] pInput the input string to split @param[in] Delimiter delimiter to split the string @param[out] pArraySize array size will be put here @retval NULL at least one of parameters is NULL or memory allocation failure @retval the split input string as an array **/ CHAR8 ** AsciiStrSplit( IN CHAR8 *pInput, IN CHAR8 Delimiter, OUT UINT32 *pArraySize ) { CHAR8 **ppArray = NULL; CHAR8 *pInputTmp = NULL; UINT32 Index = 0; UINT32 DelimiterCounter = 0; if (pInput == NULL || pArraySize == NULL) { NVDIMM_DBG("At least one of parameters is NULL."); goto Finish; } if (pInput[0] == '\0') { goto Finish; } /** Count the number of delimiter in the string **/ for (Index = 0; pInput[Index] != '\0'; Index++) { if (pInput[Index] == Delimiter) { DelimiterCounter += 1; } } /** 1. "A,B,C": 2 delimiter, 3 array elements 2. "A,B,": 2 delimiter, 2 array elements - StrTok returns NULL if there is '\0' after the last delimiter instead of empty string **/ if (pInput[Index - 1] != Delimiter) { DelimiterCounter += 1; } *pArraySize = DelimiterCounter; /** Allocate an array memory and fill it with split input string **/ ppArray = AllocateZeroPool(*pArraySize * sizeof(CHAR8 *)); if (ppArray == NULL) { NVDIMM_ERR("Memory allocation failed."); goto FinishCleanMemory; } /** Copy the input to a tmp var to avoid changing it **/ pInputTmp = AllocateZeroPool(AsciiStrSize(pInput)); if (pInputTmp == NULL) { NVDIMM_ERR("Memory allocation failed."); goto FinishCleanMemory; } AsciiStrnCpyS(pInputTmp, AsciiStrSize(pInput) / sizeof(CHAR8), pInput, (AsciiStrSize(pInput) / sizeof(CHAR8)) - 1); for (Index = 0; Index < *pArraySize; Index++) { ppArray[Index] = AsciiStrTok(&pInputTmp, Delimiter); if (ppArray[Index] == NULL) { goto FinishCleanMemory; } } /** Success path **/ goto Finish; /** Error path **/ FinishCleanMemory: FreeStringArrayAscii(ppArray, *pArraySize); ppArray = NULL; *pArraySize = 0; Finish: FREE_POOL_SAFE(pInputTmp); return ppArray; } /** First free elements of array and then free the array This does NOT set pointer to array to NULL @param[in,out] ppStringArray array of strings @param[in] ArraySize number of strings **/ VOID FreeStringArray( IN OUT CHAR16 **ppStringArray, IN UINT32 ArraySize ) { UINT32 Index = 0; if (ppStringArray == NULL) { return; } for (Index = 0; Index < ArraySize; Index++) { FREE_POOL_SAFE(ppStringArray[Index]); } FREE_POOL_SAFE(ppStringArray); } /** Copy of FreeStringArray, used for avoiding static code analysis complaint @param[in,out] ppStringArray array of strings @param[in] ArraySize number of strings **/ VOID FreeStringArrayAscii( IN OUT CHAR8 **ppStringArray, IN UINT32 ArraySize ) { UINT32 Index = 0; if (ppStringArray == NULL) { return; } for (Index = 0; Index < ArraySize; Index++) { FREE_POOL_SAFE(ppStringArray[Index]); } FREE_POOL_SAFE(ppStringArray); } /** Checks if the Config Protocol version is right. @param[in] *pConfigProtocol, instance of the protocol to check @retval EFI_SUCCESS if the version matches. @retval EFI_INVALID_PARAMETER if the passed parameter equals to NULL. @retval EFI_INCOMPATIBLE_VERSION when the version is wrong. **/ EFI_STATUS CheckConfigProtocolVersion( IN EFI_DCPMM_CONFIG2_PROTOCOL *pConfigProtocol ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CONFIG_PROTOCOL_VERSION CurrentVersion; CONFIG_PROTOCOL_VERSION OpenedProtocolVersion; if (pConfigProtocol == NULL) { goto Finish; } ZeroMem(&OpenedProtocolVersion, sizeof(OpenedProtocolVersion)); CurrentVersion.AsUint32 = NVMD_CONFIG_PROTOCOL_VERSION; NVDIMM_ENTRY(); OpenedProtocolVersion.AsUint32 = pConfigProtocol->Version; if ((OpenedProtocolVersion.Separated.Major != CurrentVersion.Separated.Major) || (OpenedProtocolVersion.Separated.Minor != CurrentVersion.Separated.Minor)) { NVDIMM_ERR("The Config Protocol version is mismatching"); ReturnCode = EFI_INCOMPATIBLE_VERSION; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** We need the GUID to find our HII handle. Instead of including the whole HII library, it is better just to declare a local copy of the GUID define and variable. **/ #define EFI_HII_CONFIG_ACCESS_PROTOCOL_GUID_TEMP \ { 0x330d4706, 0xf2a0, 0x4e4f, { 0xa3, 0x69, 0xb6, 0x6f, 0xa8, 0xd5, 0x43, 0x85 } } /** Convert all Interleave settings to string WARNING! *ppIoString can be reallocated. Calling function is responsible for its freeing. Additionally *ppIoString must be dynamically allocated. @param[in] PersistentSize - Persistent size of interleave set in DIMM @param[in] NumberOfInterleavedDimms - Number of interleaved DIMMs @param[in] ImcInterleaving - iMC interleaving bit map @param[in] ChannelInterleaving - Channel interleaving bit map @param[out] ppString - output string. **/ VOID InterleaveSettingsToString( IN UINT64 PersistentSize, IN UINT8 NumberOfInterleavedDimms, IN UINT8 ImcInterleaving, IN UINT8 ChannelInterleaving, OUT CHAR16 **ppString ) { CONST CHAR16 *pImcInterleaving = NULL; CONST CHAR16 *pChannelInterleaving = NULL; if (ppString == NULL) { NVDIMM_DBG("NULL parameter provided"); return; } if (PersistentSize == 0) { *ppString = CatSPrintClean(*ppString, L"N/A"); return; } *ppString = CatSPrintClean(*ppString, L"x%d", NumberOfInterleavedDimms); pImcInterleaving = ParseImcInterleavingValue(ImcInterleaving); pChannelInterleaving = ParseChannelInterleavingValue(ChannelInterleaving); if (pImcInterleaving == NULL || pChannelInterleaving == NULL) { FREE_POOL_SAFE(*ppString); *ppString = CatSPrint(NULL, L"Error"); return; } *ppString = CatSPrintClean(*ppString, L" - " FORMAT_STR L" IMC x " FORMAT_STR L" Channel", pImcInterleaving, pChannelInterleaving); } /** Convert Channel Interleaving value to output settings string @param[in] Interleaving - Channel Interleave BitMask @retval appropriate string @retval NULL - if Interleaving value is incorrect **/ CONST CHAR16 * ParseChannelInterleavingValue( IN UINT8 Interleaving ) { if (IS_BIT_SET_VAR(Interleaving, CHANNEL_INTERLEAVE_SIZE_64B)) { return L"64B"; } if (IS_BIT_SET_VAR(Interleaving, CHANNEL_INTERLEAVE_SIZE_128B)) { return L"128B"; } if (IS_BIT_SET_VAR(Interleaving, CHANNEL_INTERLEAVE_SIZE_256B)) { return L"256B"; } if (IS_BIT_SET_VAR(Interleaving, CHANNEL_INTERLEAVE_SIZE_4KB)) { return L"4KB"; } if (IS_BIT_SET_VAR(Interleaving, CHANNEL_INTERLEAVE_SIZE_1GB)) { return L"1GB"; } return NULL; } /** Convert iMC Interleaving value to output settings string @param[in] Interleaving - iMC Interleave BitMask @retval appropriate string @retval NULL - if Interleaving value is incorrect **/ CONST CHAR16 * ParseImcInterleavingValue( IN UINT8 Interleaving ) { if (IS_BIT_SET_VAR(Interleaving, IMC_INTERLEAVE_SIZE_64B)) { return L"64B"; } if (IS_BIT_SET_VAR(Interleaving, IMC_INTERLEAVE_SIZE_128B)) { return L"128B"; } if (IS_BIT_SET_VAR(Interleaving, IMC_INTERLEAVE_SIZE_256B)) { return L"256B"; } if (IS_BIT_SET_VAR(Interleaving, IMC_INTERLEAVE_SIZE_4KB)) { return L"4KB"; } if (IS_BIT_SET_VAR(Interleaving, IMC_INTERLEAVE_SIZE_1GB)) { return L"1GB"; } return NULL; } /** Appends a formatted Unicode string to a Null-terminated Unicode string This function appends a formatted Unicode string to the Null-terminated Unicode string specified by pString. pString is optional and may be NULL. Storage for the formatted Unicode string returned is allocated using AllocatePool(). The pointer to the appended string is returned. The caller is responsible for freeing the returned string. This function also calls FreePool on the old pString buffer if it is not NULL. So the caller does not need to free the previous buffer. If pString is not NULL and not aligned on a 16-bit boundary, then ASSERT(). If pFormatString is NULL, then ASSERT(). If pFormatString is not aligned on a 16-bit boundary, then ASSERT(). @param[in] pString A Null-terminated Unicode string. @param[in] pFormatString A Null-terminated Unicode format string. @param[in] ... The variable argument list whose contents are accessed based on the format string specified by pFormatString. @retval NULL There was not enough available memory. @return Null-terminated Unicode string is that is the formatted string appended to pString. **/ CHAR16* EFIAPI CatSPrintClean( IN CHAR16 *pString, OPTIONAL IN CONST CHAR16 *pFormatString, ... ) { CHAR16 *pResult = NULL; VA_LIST ArgList; VA_START(ArgList, pFormatString); pResult = CatVSPrint(pString, pFormatString, ArgList); VA_END(ArgList); FREE_POOL_SAFE(pString); return pResult; } /** Appends a formatted Unicode string with arguments to a pre-allocated null-terminated Unicode string provided by the caller with length of DestStringMaxLength. @param[in] DestString A Null-terminated Unicode string of size DestStringMaxLength @param[in] DestStringMaxLength The maximum number of CHAR16 characters that will fit into DestString @param[in] FormatString A Null-terminated Unicode format string. @param[in] ... The variable argument list whose contents are accessed based on the format string specified by FormatString. **/ EFI_STATUS CatSPrintNCopy( IN OUT CHAR16 *pDestString, IN UINT16 DestStringMaxLength, IN CONST CHAR16 *pFormatString, ... ) { VA_LIST Marker; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CHAR16 *pNewString = NULL; CHECK_NULL_ARG(pDestString, Finish); CHECK_NULL_ARG(pFormatString, Finish); VA_START(Marker, pFormatString); CHECK_RESULT_MALLOC(pNewString, CatVSPrint(L"", pFormatString, Marker), Finish); VA_END(Marker); // Don't care about the length of the source new string, as we are freeing it CHECK_RESULT(StrCatS(pDestString, DestStringMaxLength, pNewString), Finish); Finish: FREE_POOL_SAFE(pNewString); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Function to write a line of unicode text to a file. if Handle is NULL, return error. if Buffer is NULL, return error. @param[in] Handle FileHandle to write to @param[in] Buffer Buffer to write @retval EFI_SUCCESS The data was written. @retval other Error codes from Write function. **/ EFI_STATUS EFIAPI WriteAsciiLine( IN EFI_FILE_HANDLE Handle, IN VOID *pBuffer ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINTN Size = 0; if (pBuffer == NULL || StrSize(pBuffer) < sizeof(CHAR16)) { ReturnCode = EFI_INVALID_PARAMETER; return ReturnCode; } if (Handle == NULL) { ReturnCode = EFI_INVALID_PARAMETER; return ReturnCode; } // Removing the '\0' char from buffer Size = AsciiStrSize(pBuffer) - sizeof(CHAR8); ReturnCode = Handle->Write(Handle, &Size, (CHAR8 *)pBuffer); return ReturnCode; } /** Try to find a sought pointer in an array @param[in] pPointersArray Array of pointers @param[in] PointersNum Number of pointers in array @param[in] pSoughtPointer Sought pointer @retval TRUE if pSoughtPointer has been found in the array @retval FALSE otherwise **/ BOOLEAN IsPointerInArray( IN VOID *pPointersArray[], IN UINT32 PointersNum, IN VOID *pSoughtPointer ) { UINT32 Index = 0; if (pPointersArray == NULL || pSoughtPointer == NULL) { return FALSE; } for (Index = 0; Index < PointersNum; Index++) { if (pSoughtPointer == pPointersArray[Index]) { return TRUE; } } return FALSE; } /** Check if given language is supported (is on supported language list) @param[in] pSupportedLanguages - list of supported languages @param[in] pLanguage - language to verify if is supported @param[in] Rfc4646Language - language abbreviation is compatible with Rfc4646 standard @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_UNSUPPORTED - language is not supported @retval EFI_SUCCESS Is supported **/ EFI_STATUS CheckIfLanguageIsSupported( IN CONST CHAR8 *pSupportedLanguages, IN CONST CHAR8 *pLanguage, IN BOOLEAN Rfc4646Language ) { CHAR8 CONST *pSupportedLanguageTmp = pSupportedLanguages; BOOLEAN Found = FALSE; UINT16 Index = 0; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pSupportedLanguages == NULL || pLanguage == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("Invalid language parameter given"); goto Finish; } while (pSupportedLanguageTmp[0] != '\0') { if (Rfc4646Language) { /** Languages are separated by ';' **/ for (Index = 0; pSupportedLanguageTmp[Index] != 0 && pSupportedLanguageTmp[Index] != ';'; Index++); if ((AsciiStrnCmp(pSupportedLanguageTmp, pLanguage, Index) == 0) && (pLanguage[Index] == '\0')) { Found = TRUE; break; } pSupportedLanguageTmp += Index; for (; pSupportedLanguageTmp[0] != 0 && pSupportedLanguageTmp[0] == ';'; pSupportedLanguageTmp++); } else { /** Languages are 2 digits length separated by space **/ if (CompareMem(pLanguage, pSupportedLanguageTmp, NOT_RFC4646_ABRV_LANGUAGE_LEN) == 0) { Found = TRUE; break; } pSupportedLanguageTmp += NOT_RFC4646_ABRV_LANGUAGE_LEN; } } if (!Found) { NVDIMM_DBG("Language (%s) was not found in supported language list (%s)", pLanguage, pSupportedLanguages); ReturnCode = EFI_UNSUPPORTED; goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Convert a character to upper case @param[in] InChar - character to up @retval - upper character **/ CHAR16 NvmToUpper( IN CHAR16 InChar ) { CHAR16 upper = InChar; if (InChar >= L'a' && InChar <= L'z') { upper = (CHAR16) (InChar - (L'a' - L'A')); } return upper; } /** Case Insensitive StrCmp @param[in] pFirstString - first string for comparison @param[in] pSecondString - second string for comparison @retval Negative number if strings don't match and pFirstString < pSecondString @retval 0 if strings match @retval Positive number if strings don't match and pFirstString > pSecondString **/ INTN StrICmp( IN CONST CHAR16 *pFirstString, IN CONST CHAR16 *pSecondString ) { INTN Result = -1; if (pFirstString != NULL && pSecondString != NULL && StrLen(pFirstString) != 0 && StrLen(pSecondString) != 0 && StrSize(pFirstString) == StrSize(pSecondString)) { while (*pFirstString != L'\0' && NvmToUpper(*pFirstString) == NvmToUpper(*pSecondString)) { pFirstString++; pSecondString++; } Result = *pFirstString - *pSecondString; } return Result; } /** Calculate a power of base. @param[in] Base base @param[in] Exponent exponent @retval Base ^ Exponent **/ UINT64 Pow( IN UINT64 Base, IN UINT32 Exponent ) { UINT64 Result = Base; UINT32 Index = 0; NVDIMM_ENTRY(); if (Exponent == 0) { return 1; } for (Index = 1; Index < Exponent; Index++) { Result *= Base; } NVDIMM_EXIT(); return Result; } /** Clear memory containing string @param[in] pString - pointer to string to be cleared **/ VOID CleanStringMemory( IN CHAR8 *pString ) { if (pString == NULL) { return; } while (*pString != '\0') { *pString = '\0'; ++pString; } } /** Clear memory containing unicode string @param[in] pString - pointer to string to be cleared **/ VOID CleanUnicodeStringMemory( IN CHAR16 *pString ) { if (pString == NULL) { return; } while (*pString != L'\0') { *pString = L'\0'; ++pString; } } /** Get linked list size @param[in] pListHead List head @param[out] pListSize Counted number of items in the list @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER At least one of the input parameters equals NULL **/ EFI_STATUS GetListSize( IN LIST_ENTRY *pListHead, OUT UINT32 *pListSize ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LIST_ENTRY *pNode = NULL; if (pListHead == NULL || pListSize == NULL) { goto Finish; } *pListSize = 0; LIST_FOR_EACH(pNode, pListHead) { (*pListSize)++; } ReturnCode = EFI_SUCCESS; Finish: return ReturnCode; } /** Implementation of public algorithm to calculate least common multiple of two numbers @param[in] A First number @param[in] B Second number @retval Least common multiple **/ UINT64 FindLeastCommonMultiple( IN UINT64 A, IN UINT64 B ) { UINT64 LeastCommonMultiple = 0; UINT64 WarrantedCommonMultiple = A * B; UINT64 Tmp = 0; while (B != 0) { Tmp = B; B = A % B; A = Tmp; } LeastCommonMultiple = WarrantedCommonMultiple / A; return LeastCommonMultiple; } /** Trim white spaces from the begin and end of string @param[in, out] pString Null terminated string that will be trimmed @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameters is NULL @retval EFI_BAD_BUFFER_SIZE Size of input string is bigger than MAX_INT32 **/ EFI_STATUS TrimString( IN OUT CHAR16 *pString ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINTN LengthTmp = 0; INT32 Length = 0; INT32 Index = 0; INT32 Offset = 0; BOOLEAN WhiteSpace = FALSE; NVDIMM_ENTRY(); if (pString == NULL) { goto Finish; } LengthTmp = StrLen(pString); if (LengthTmp > MAX_INT32) { ReturnCode = EFI_BAD_BUFFER_SIZE; goto Finish; } Length = (INT32) LengthTmp; if (Length > 0) { /** Trim white spaces at the end of string **/ for (Index = Length - 1; Index >= 0; Index--) { if (IS_WHITE_UNICODE(pString[Index])) { pString[Index] = L'\0'; Length--; } else { break; } } /** Trim white spaces at the begin of string **/ WhiteSpace = TRUE; for (Index = 0; Index < Length; Index++) { if (WhiteSpace && !IS_WHITE_UNICODE(pString[Index])) { if (Index == 0) { /** There is no white spaces at the begin of string, so skip this stage **/ break; } Offset = Index; WhiteSpace = FALSE; } if (!WhiteSpace) { pString[Index - Offset] = pString[Index]; } } pString[Length - Offset] = '\0'; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Removes all white spaces from string @param[in] pInputBuffer Pointer to string to remove white spaces @param[out] pOutputBuffer Pointer to string with no white spaces @param[in, out] OutputBufferLength On input, length of buffer (in CHAR16), on output, length of string with no white spaces, without null-terminator @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL or string length is 0 @retval EFI_BUFFER_TOO_SMALL Output buffer is too small **/ EFI_STATUS RemoveWhiteSpaces( IN CHAR8 *pInputBuffer, OUT CHAR8 *pOutputBuffer, IN OUT UINT64 *pOutputBufferLength ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT64 InputBuffLength = 0; UINT64 Index = 0; UINT64 Index2 = 0; NVDIMM_ENTRY(); if (pInputBuffer == NULL || pOutputBuffer == NULL) { NVDIMM_DBG("Invalid pointer"); goto Finish; } InputBuffLength = AsciiStrLen(pInputBuffer); if (InputBuffLength == 0) { NVDIMM_DBG("Line empty, nothing to remove."); goto Finish; } // Output buffer needs to have place for null terminator if (*pOutputBufferLength - 1 < InputBuffLength) { NVDIMM_DBG("Invalid buffer length"); return EFI_BUFFER_TOO_SMALL; } for (Index = 0; Index < InputBuffLength; Index++) { if (!IS_WHITE_ASCII(pInputBuffer[Index])) { pOutputBuffer[Index2] = pInputBuffer[Index]; Index2++; } } *pOutputBufferLength = Index2; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Convert Last Shutdown Status to string @param[in] LastShutdownStatus structure @param[in] FwVer Struct representing firmware version @retval CLI string representation of last shutdown status **/ CHAR16* LastShutdownStatusToStr( IN LAST_SHUTDOWN_STATUS_DETAILS_COMBINED LastShutdownStatus, IN FIRMWARE_VERSION FwVer ) { CHAR16 *pStatusStr = NULL; NVDIMM_ENTRY(); if (LastShutdownStatus.Combined.LastShutdownStatus.Separated.PmAdr) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR, LAST_SHUTDOWN_STATUS_PM_ADR_STR); } if (LastShutdownStatus.Combined.LastShutdownStatus.Separated.PmS3) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_PM_S3_STR); } if (LastShutdownStatus.Combined.LastShutdownStatus.Separated.PmS5) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_PM_S5_STR); } if (LastShutdownStatus.Combined.LastShutdownStatus.Separated.DdrtPowerFailure) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_DDRT_POWER_FAIL_STR); } if (LastShutdownStatus.Combined.LastShutdownStatus.Separated.PmicPowerLoss) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_PMIC_POWER_LOSS_STR); } if (LastShutdownStatus.Combined.LastShutdownStatus.Separated.PmWarmReset) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_PM_WARM_RESET_STR); } if (LastShutdownStatus.Combined.LastShutdownStatus.Separated.ThermalShutdown) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_THERMAL_SHUTDOWN_STR); } if (FwVer.FwApiMajor < 3 && LastShutdownStatus.Combined.LastShutdownStatus.Separated.FwFlushComplete) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_FW_FLUSH_COMPLETE_STR); } if (LastShutdownStatus.Combined.LastShutdownStatusExtended.Separated.ViralInterrupt) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_VIRAL_INTERRUPT_STR); } if (LastShutdownStatus.Combined.LastShutdownStatusExtended.Separated.SurpriseClockStopInterrupt) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_SURPRISE_CLOCK_STOP_INTERRUPT_STR); } if (LastShutdownStatus.Combined.LastShutdownStatusExtended.Separated.WriteDataFlushComplete) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_WRITE_DATA_FLUSH_COMPLETE_STR); } if (LastShutdownStatus.Combined.LastShutdownStatusExtended.Separated.S4PowerState) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_S4_POWER_STATE_STR); } // Output SRE Clock Stop Received at the same time as PM Idle Received for backwards compatibility if (LastShutdownStatus.Combined.LastShutdownStatusExtended.Separated.PMIdle) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_PM_IDLE_STR, L", ", LAST_SHUTDOWN_STATUS_SRE_CLOCK_STOP_STR); } if (LastShutdownStatus.Combined.LastShutdownStatusExtended.Separated.DdrtSurpriseReset) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_SURPRISE_RESET_STR); } if (LastShutdownStatus.Combined.LastShutdownStatusExtended.Separated.EnhancedAdrFlushStatus == EXTENDED_ADR_FLUSH_COMPLETE) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_ENHANCED_ADR_FLUSH_COMPLETE_STR); } else { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_ENHANCED_ADR_FLUSH_NOT_COMPLETE_STR); } if (LastShutdownStatus.Combined.LastShutdownStatusExtended.Separated.SxExtendedFlushStatus == SX_EXTENDED_FLUSH_COMPLETE) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_ENHANCED_SX_EXTENDED_FLUSH_COMPLETE_STR); } else { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR FORMAT_STR, pStatusStr == NULL ? L"" : L", ", LAST_SHUTDOWN_STATUS_ENHANCED_SX_EXTENDED_FLUSH_NOT_COMPLETE_STR); } if (pStatusStr == NULL) { pStatusStr = CatSPrintClean(pStatusStr, FORMAT_STR, LAST_SHUTDOWN_STATUS_UNKNOWN_STR); } NVDIMM_EXIT(); return pStatusStr; } /** Converts the dimm health state reason to its HII string equivalent @param[in] HiiHandle - handle for hii @param[in] HealthStateReason The health state reason to be converted into its HII string @param[out] ppHealthStateStr A pointer to the HII health state string. Dynamically allocated memory and must be released by calling function. @retval EFI_OUT_OF_RESOURCES if there is no space available to allocate memory for string @retval EFI_INVALID_PARAMETER if one or more input parameters are invalid @retval EFI_SUCCESS The conversion was successful **/ EFI_STATUS ConvertHealthStateReasonToHiiStr( IN EFI_HANDLE HiiHandle, IN UINT16 HealthStatusReason, OUT CHAR16 **ppHealthStatusReasonStr ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT16 mask = BIT0; CHAR16 *NextSubString = NULL; NVDIMM_ENTRY(); if (ppHealthStatusReasonStr == NULL) { goto Finish; } *ppHealthStatusReasonStr = NULL; while (mask <= BIT9) { switch (HealthStatusReason & mask) { case HEALTH_REASON_PERCENTAGE_REMAINING_LOW: NextSubString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_VIEW_DCPMM_FORM_PERCENTAGE_REMAINING), NULL); break; case HEALTH_REASON_PACKAGE_SPARING_HAS_HAPPENED: NextSubString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_VIEW_DCPMM_PACKAGE_SPARING_HAPPENED), NULL); break; case HEALTH_REASON_CAP_SELF_TEST_WARNING: NextSubString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_VIEW_DCPMM_FORM_CAP_SELF_TEST_WARNING), NULL); break; case HEALTH_REASON_PERC_REMAINING_EQUALS_ZERO: NextSubString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_VIEW_DCPMM_FORM_PERCENTAGE_REMAINING_ZERO), NULL); break; case HEALTH_REASON_DIE_FAILURE: NextSubString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_VIEW_DCPMM_FORM_DIE_FAILURE), NULL); break; case HEALTH_REASON_AIT_DRAM_DISABLED: NextSubString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_VIEW_DCPMM_FORM_AIT_DRAM_DISABLED), NULL); break; case HEALTH_REASON_CAP_SELF_TEST_FAILURE: NextSubString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_VIEW_DCPMM_FORM_CAP_SELF_TEST_FAIL), NULL); break; case HEALTH_REASON_CRITICAL_INTERNAL_STATE_FAILURE: NextSubString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_VIEW_DCPMM_FORM_CRITICAL_INTERNAL_FAILURE), NULL); break; case HEALTH_REASON_PERFORMANCE_DEGRADED: NextSubString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_VIEW_DCPMM_FORM_PERFORMANCE_DEGRADED), NULL); break; case HEALTH_REASON_CAP_SELF_TEST_COMM_FAILURE: NextSubString = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_VIEW_DCPMM_FORM_CAP_SELF_TEST_COMM_FAILURE), NULL); break; } if (HealthStatusReason & mask) { *ppHealthStatusReasonStr = CatSPrintClean(*ppHealthStatusReasonStr, ((*ppHealthStatusReasonStr == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), NextSubString); FREE_POOL_SAFE(NextSubString); } mask = mask << 1; } if (*ppHealthStatusReasonStr == NULL) { *ppHealthStatusReasonStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_VIEW_DCPMM_FORM_NONE), NULL); } if (*ppHealthStatusReasonStr == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get Dimm Info by device handle Scan the dimm list for a DimmInfo identified by device handle @param[in] DeviceHandle Device Handle of the dimm @param[in] pDimmInfo Array of DimmInfo @param[in] DimmCount Size of DimmInfo array @param[out] ppRequestedDimmInfo Pointer to the request DimmInfo struct @retval EFI_INVALID_PARAMETER pDimmInfo or pRequestedDimmInfo is NULL @retval EFI_SUCCESS Success **/ EFI_STATUS GetDimmInfoByHandle( IN UINT32 DeviceHandle, IN DIMM_INFO *pDimmInfo, IN UINT32 DimmCount, OUT DIMM_INFO **ppRequestedDimmInfo ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; NVDIMM_ENTRY(); if (pDimmInfo == NULL || ppRequestedDimmInfo == NULL) { goto Finish; } for (Index = 0; Index < DimmCount; Index++) { if (DeviceHandle == pDimmInfo[Index].DimmHandle) { *ppRequestedDimmInfo = &pDimmInfo[Index]; } } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Converts the Dimm IDs within a region to its HII string equivalent @param[in] pRegionInfo The Region info with DimmID and DimmCount its HII string @param[in] pNvmDimmConfigProtocol A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] DimmIdentifier Dimm identifier preference @param[out] ppDimmIdStr A pointer to the HII DimmId string. Dynamically allocated memory and must be released by calling function. @retval EFI_OUT_OF_RESOURCES if there is no space available to allocate memory for string @retval EFI_INVALID_PARAMETER if one or more input parameters are invalid @retval EFI_SUCCESS The conversion was successful **/ EFI_STATUS ConvertRegionDimmIdsToDimmListStr( IN REGION_INFO *pRegionInfo, IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN UINT8 DimmIdentifier, OUT CHAR16 **ppDimmIdStr ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; INT32 Index = 0; DIMM_INFO *pDimmInfo = NULL; UINT32 DimmCount = 0; DIMM_INFO *pDimmList = NULL; NVDIMM_ENTRY(); if (pRegionInfo == NULL || pNvmDimmConfigProtocol == NULL || ppDimmIdStr == NULL) { goto Finish; } *ppDimmIdStr = NULL; ReturnCode = pNvmDimmConfigProtocol->GetDimmCount(pNvmDimmConfigProtocol, &DimmCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Communication with driver failed"); goto Finish; } pDimmList = AllocateZeroPool(sizeof(*pDimmList) * DimmCount); if (pDimmList == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_DBG("Could not allocate memory"); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetDimms(pNvmDimmConfigProtocol, DimmCount, DIMM_INFO_CATEGORY_NONE, pDimmList); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Communication with driver failed"); goto Finish; } for (Index = 0; Index < pRegionInfo->DimmIdCount; Index++) { if (DimmIdentifier == DISPLAY_DIMM_ID_HANDLE) { *ppDimmIdStr = CatSPrintClean(*ppDimmIdStr, ((*ppDimmIdStr == NULL) ? FORMAT_HEX : FORMAT_HEX_WITH_COMMA), pRegionInfo->DimmId[Index]); } else { ReturnCode = GetDimmInfoByHandle(pRegionInfo->DimmId[Index], pDimmList, DimmCount, &pDimmInfo); if (EFI_ERROR(ReturnCode)) { FREE_POOL_SAFE(*ppDimmIdStr); NVDIMM_DBG("Failed to retrieve DimmInfo by Device Handle"); goto Finish; } *ppDimmIdStr = CatSPrintClean(*ppDimmIdStr, ((*ppDimmIdStr == NULL) ? FORMAT_STR : FORMAT_STR_WITH_COMMA), pDimmInfo->DimmUid); } } if (*ppDimmIdStr == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pDimmList); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Convert modes supported by to string @param[in] ModesSupported, bits define modes supported @retval CLI string representation of memory modes supported **/ CHAR16* ModesSupportedToStr( IN UINT8 ModesSupported ) { CHAR16 *pModesStr = NULL; NVDIMM_ENTRY(); if (ModesSupported & BIT0) { pModesStr = CatSPrintClean(pModesStr, FORMAT_STR, MODES_SUPPORTED_MEMORY_MODE_STR); } if (ModesSupported & BIT2) { pModesStr = CatSPrintClean(pModesStr, FORMAT_STR FORMAT_STR, pModesStr == NULL ? L"" : L", ", MODES_SUPPORTED_APP_DIRECT_MODE_STR); } NVDIMM_EXIT(); return pModesStr; } /** Convert software triggers enabled to string @param[in] SoftwareTriggersEnabled, bits define triggers that are enabled @retval CLI string representation of enabled triggers **/ CHAR16* SoftwareTriggersEnabledToStr( IN UINT64 SoftwareTriggersEnabled ) { CHAR16 *pModesStr = NULL; if (!SoftwareTriggersEnabled) { pModesStr = CatSPrintClean(pModesStr, FORMAT_STR, SW_TRIGGERS_ENABLED_NONE_STR); } else { if (SoftwareTriggersEnabled & BIT0) { pModesStr = CatSPrintClean(pModesStr, FORMAT_STR, SW_TRIGGERS_ENABLED_BIT0_STR); } if (SoftwareTriggersEnabled & BIT1) { pModesStr = CatSPrintClean(pModesStr, FORMAT_STR FORMAT_STR, pModesStr == NULL ? L"" : L", ", SW_TRIGGERS_ENABLED_BIT1_STR); } if (SoftwareTriggersEnabled & BIT2) { pModesStr = CatSPrintClean(pModesStr, FORMAT_STR FORMAT_STR, pModesStr == NULL ? L"" : L", ", SW_TRIGGERS_ENABLED_BIT2_STR); } if (SoftwareTriggersEnabled & BIT3) { pModesStr = CatSPrintClean(pModesStr, FORMAT_STR FORMAT_STR, pModesStr == NULL ? L"" : L", ", SW_TRIGGERS_ENABLED_BIT3_STR); } if (SoftwareTriggersEnabled & BIT4) { pModesStr = CatSPrintClean(pModesStr, FORMAT_STR FORMAT_STR, pModesStr == NULL ? L"" : L", ", SW_TRIGGERS_ENABLED_BIT4_STR); } } return pModesStr; } /** Convert Security Capabilities to string @param[in] SecurityCapabilities, bits define capabilities @retval CLI string representation of security capabilities **/ CHAR16* SecurityCapabilitiesToStr( IN UINT8 SecurityCapabilities ) { CHAR16 *pCapabilitiesStr = NULL; NVDIMM_ENTRY(); if (SecurityCapabilities & BIT0) { pCapabilitiesStr = CatSPrintClean(pCapabilitiesStr, FORMAT_STR, SECURITY_CAPABILITIES_ENCRYPTION); } if (SecurityCapabilities & BIT1) { if (pCapabilitiesStr != NULL) { pCapabilitiesStr = CatSPrintClean(pCapabilitiesStr, FORMAT_STR, L", "); } pCapabilitiesStr = CatSPrintClean(pCapabilitiesStr, FORMAT_STR, SECURITY_CAPABILITIES_ERASE); } else if (SecurityCapabilities == 0) { pCapabilitiesStr = CatSPrintClean(pCapabilitiesStr, FORMAT_STR, SECURITY_CAPABILITIES_NONE); } NVDIMM_EXIT(); return pCapabilitiesStr; } /** Convert Dimm security state to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] Dimm security state @retval String representation of Dimm's security state **/ CHAR16* SecurityToString( IN EFI_HANDLE HiiHandle, IN UINT8 SecurityState ) { CHAR16 *pSecurityString = NULL; CHAR16 *pTempStr = NULL; switch (SecurityState) { case SECURITY_DISABLED: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_DISABLED), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case SECURITY_LOCKED: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_LOCKED), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case SECURITY_UNLOCKED: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_UNLOCKED), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case SECURITY_PW_MAX: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_PW_MAX), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case SECURITY_MASTER_PW_MAX: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_MASTER_PW_MAX), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case SECURITY_FROZEN: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_FROZEN), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case SECURITY_NOT_SUPPORTED: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_NOT_SUPPORTED), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; default: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_UNKNOWN), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; } return pSecurityString; } /** Convert dimm's security state bitmask to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] SecurityStateBitmask, bits define dimm's security state @retval String representation of Dimm's security state **/ CHAR16* SecurityStateBitmaskToString( IN EFI_HANDLE HiiHandle, IN UINT32 SecurityStateBitmask ) { CHAR16 *pSecurityString = NULL; CHAR16 *pTempStr = NULL; if (SecurityStateBitmask & SECURITY_MASK_NOT_SUPPORTED) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_NOT_SUPPORTED), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); goto Finish; } if (SecurityStateBitmask & SECURITY_MASK_ENABLED) { if (SecurityStateBitmask & SECURITY_MASK_LOCKED) { // Security State = Locked pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_LOCKED), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); } else { // Security State = Unlocked pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_UNLOCKED), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); } } else { // Security State = Disabled pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_DISABLED), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); } if (SecurityStateBitmask & SECURITY_MASK_COUNTEXPIRED) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_PW_MAX), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR FORMAT_STR, L", ", pTempStr); FREE_POOL_SAFE(pTempStr); } if (SecurityStateBitmask & SECURITY_MASK_MASTER_COUNTEXPIRED) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_MASTER_PW_MAX), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR FORMAT_STR, L", ", pTempStr); FREE_POOL_SAFE(pTempStr); } if (SecurityStateBitmask & SECURITY_MASK_FROZEN) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SECSTATE_FROZEN), NULL); pSecurityString = CatSPrintClean(pSecurityString, FORMAT_STR FORMAT_STR, L", ", pTempStr); FREE_POOL_SAFE(pTempStr); } Finish: return pSecurityString; } /** Convert dimm's SVN Downgrade Opt-In to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] SecurityOptIn, bits define dimm's security opt-in value @retval String representation of Dimm's SVN Downgrade opt-in **/ CHAR16* SVNDowngradeOptInToString( IN EFI_HANDLE HiiHandle, IN UINT32 OptInValue ) { CHAR16 *pOptIntString = NULL; CHAR16 *pTempStr = NULL; switch (OptInValue) { case SVN_DOWNGRADE_DISABLE: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SEC_OPTIN_SVN_DOWNGRADE_DISABLED), NULL); pOptIntString = CatSPrintClean(pOptIntString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case SVN_DOWNGRADE_ENABLE: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SEC_OPTIN_SVN_DOWNGRADE_ENABLED), NULL); pOptIntString = CatSPrintClean(pOptIntString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; default: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SEC_OPTIN_UNKNOWN), NULL); pOptIntString = CatSPrintClean(pOptIntString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; } return pOptIntString; } /** Convert dimm's Secure Erase Policy Opt-In to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] SecurityOptIn, bits define dimm's security opt-in value @retval String representation of Dimm's Secure Erase Policy opt-in **/ CHAR16* SecureErasePolicyOptInToString( IN EFI_HANDLE HiiHandle, IN UINT32 OptInValue ) { CHAR16 *pOptIntString = NULL; CHAR16 *pTempStr = NULL; switch (OptInValue) { case SECURE_ERASE_NOT_OPTED_IN: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SEC_OPTIN_SECURE_ERASE_NO_MASTER_PASSPHRASE), NULL); pOptIntString = CatSPrintClean(pOptIntString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case SECURE_ERASE_OPTED_IN: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SEC_OPTIN_SECURE_ERASE_MASTER_PASSPHRASE_ENABLED), NULL); pOptIntString = CatSPrintClean(pOptIntString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; default: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SEC_OPTIN_UNKNOWN), NULL); pOptIntString = CatSPrintClean(pOptIntString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; } return pOptIntString; } /** Convert dimm's S3 Resume Opt-In to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] SecurityOptIn, bits define dimm's security opt-in value @retval String representation of Dimm's S3 Resume opt-in **/ CHAR16* S3ResumeOptInToString( IN EFI_HANDLE HiiHandle, IN UINT32 OptInValue ) { CHAR16 *pOptIntString = NULL; CHAR16 *pTempStr = NULL; switch (OptInValue) { case S3_RESUME_SECURE_S3: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SEC_OPTIN_SECURE_S3), NULL); pOptIntString = CatSPrintClean(pOptIntString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case S3_RESUME_UNSECURE_S3: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SEC_OPTIN_UNSECURE_S3), NULL); pOptIntString = CatSPrintClean(pOptIntString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; default: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SEC_OPTIN_UNKNOWN), NULL); pOptIntString = CatSPrintClean(pOptIntString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; } return pOptIntString; } /** Convert dimm's Fw Activate Opt-In to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] SecurityOptIn, bits define dimm's security opt-in value @retval String representation of Dimm's Fw Activate opt-in **/ CHAR16* FwActivateOptInToString( IN EFI_HANDLE HiiHandle, IN UINT32 OptInValue ) { CHAR16 *pOptIntString = NULL; CHAR16 *pTempStr = NULL; switch (OptInValue) { case FW_ACTIVATE_DISABLED: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SEC_OPTIN_FW_ACTIVATE_DISABLED), NULL); pOptIntString = CatSPrintClean(pOptIntString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case FW_ACTIVATE_ENABLED: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SEC_OPTIN_FW_ACTIVATE_ENABLED), NULL); pOptIntString = CatSPrintClean(pOptIntString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; default: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_SEC_OPTIN_UNKNOWN), NULL); pOptIntString = CatSPrintClean(pOptIntString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; } return pOptIntString; } /** Convert long op status value to its respective string @param[in] HiiHandle Pointer to HII handle @param[in] LongOpStatus status value @retval CLI string representation of long op status **/ CHAR16* LongOpStatusToStr( IN EFI_HANDLE HiiHandle, IN UINT8 LongOpStatus ) { CHAR16 *pLongOpStatusStr = NULL; NVDIMM_ENTRY(); switch (LongOpStatus) { case LONG_OP_STATUS_NOT_STARTED: pLongOpStatusStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_LONG_OP_STATUS_NOT_STARTED), NULL); break; case LONG_OP_STATUS_IN_PROGRESS: pLongOpStatusStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_LONG_OP_STATUS_IN_PROGRESS), NULL); break; case LONG_OP_STATUS_COMPLETED: pLongOpStatusStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_LONG_OP_STATUS_COMPLETED), NULL); break; case LONG_OP_STATUS_ABORTED: pLongOpStatusStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_LONG_OP_STATUS_ABORTED), NULL); break; case LONG_OP_STATUS_UNKNOWN: pLongOpStatusStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_LONG_OP_STATUS_UNKNOWN), NULL); break; case LONG_OP_STATUS_ERROR: default: pLongOpStatusStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_LONG_OP_STATUS_ERROR), NULL); break; } NVDIMM_EXIT(); return pLongOpStatusStr; } /** Convert dimm's boot status bitmask to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] BootStatusBitmask, bits define the boot status @retval CLI/HII string representation of dimm's boot status **/ CHAR16* BootStatusBitmaskToStr( IN EFI_HANDLE HiiHandle, IN UINT16 BootStatusBitmask ) { CHAR16 *pBootStatusStr = NULL; CHAR16 *pTempStr = NULL; NVDIMM_ENTRY(); if (DIMM_BOOT_STATUS_NORMAL == BootStatusBitmask) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_BOOT_STATUS_SUCCESS), NULL); pBootStatusStr = CatSPrintClean(pBootStatusStr, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); } else if (BootStatusBitmask & DIMM_BOOT_STATUS_UNKNOWN) { if (BootStatusBitmask & DIMM_BOOT_STATUS_INTERFACE_UNKNOWN) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_BOOT_STATUS_INTERFACE_UNKNOWN), NULL); pBootStatusStr = CatSPrintClean(pBootStatusStr, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); } if (BootStatusBitmask & DIMM_BOOT_STATUS_BSR_UNKNOWN) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_BOOT_STATUS_BSR_UNKNOWN), NULL); pBootStatusStr = CatSPrintClean(pBootStatusStr, FORMAT_STR FORMAT_STR, pBootStatusStr == NULL ? L"" : L", ", pTempStr); FREE_POOL_SAFE(pTempStr); } } else { if (BootStatusBitmask & DIMM_BOOT_STATUS_MEDIA_NOT_READY) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_BOOT_STATUS_MEDIA_NOT_READY), NULL); pBootStatusStr = CatSPrintClean(pBootStatusStr, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); } if (BootStatusBitmask & DIMM_BOOT_STATUS_MEDIA_ERROR) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_BOOT_STATUS_MEDIA_ERROR), NULL); pBootStatusStr = CatSPrintClean(pBootStatusStr, FORMAT_STR FORMAT_STR, pBootStatusStr == NULL ? L"" : L", ", pTempStr); FREE_POOL_SAFE(pTempStr); } if (BootStatusBitmask & DIMM_BOOT_STATUS_MEDIA_DISABLED) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_BOOT_STATUS_MEDIA_DISABLED), NULL); pBootStatusStr = CatSPrintClean(pBootStatusStr, FORMAT_STR FORMAT_STR, pBootStatusStr == NULL ? L"" : L", ", pTempStr); FREE_POOL_SAFE(pTempStr); } if (BootStatusBitmask & DIMM_BOOT_STATUS_DDRT_NOT_READY) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_BOOT_STATUS_DDRT_NOT_READY), NULL); pBootStatusStr = CatSPrintClean(pBootStatusStr, FORMAT_STR FORMAT_STR, pBootStatusStr == NULL ? L"" : L", ", pTempStr); FREE_POOL_SAFE(pTempStr); } if (BootStatusBitmask & DIMM_BOOT_STATUS_SMBUS_NOT_READY) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_BOOT_STATUS_SMBUS_NOT_READY), NULL); pBootStatusStr = CatSPrintClean(pBootStatusStr, FORMAT_STR FORMAT_STR, pBootStatusStr == NULL ? L"" : L", ", pTempStr); FREE_POOL_SAFE(pTempStr); } if (BootStatusBitmask & DIMM_BOOT_STATUS_MAILBOX_NOT_READY) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_BOOT_STATUS_MAILBOX_NOT_READY), NULL); pBootStatusStr = CatSPrintClean(pBootStatusStr, FORMAT_STR FORMAT_STR, pBootStatusStr == NULL ? L"" : L", ", pTempStr); FREE_POOL_SAFE(pTempStr); } if (BootStatusBitmask & DIMM_BOOT_STATUS_REBOOT_REQUIRED) { pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_BOOT_STATUS_RR), NULL); pBootStatusStr = CatSPrintClean(pBootStatusStr, FORMAT_STR FORMAT_STR, pBootStatusStr == NULL ? L"" : L", ", pTempStr); FREE_POOL_SAFE(pTempStr); } } NVDIMM_EXIT(); return pBootStatusStr; } /** Convert string value to double @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] pString String value to convert @param[out] pOutValue Target double value @retval EFI_INVALID_PARAMETER No valid value inside @retval EFI_SUCCESS Conversion successful **/ EFI_STATUS StringToDouble( IN EFI_HANDLE HiiHandle, IN CHAR16 *pString, OUT double *pOutValue ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CHAR16 *pLastChar = NULL; CHAR16 **ppStringElements = NULL; UINT64 DecimalElements[2] = {0}; UINT32 ElementsCount = 0; UINT32 Index = 0; BOOLEAN Valid = 0; double Decimal = 0.0; double Fractional = 0.0; CHAR16 *pDecimalMarkStr = NULL; NVDIMM_ENTRY(); if (pString == NULL || pOutValue == NULL) { goto Finish; } pDecimalMarkStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_DECIMAL_MARK), NULL); if (pDecimalMarkStr == NULL) { ReturnCode = EFI_NOT_FOUND; goto Finish; } // Ignore leading white chars while ((*pString == L' ') || (*pString == L'\t')) { pString++; } // Delete trailing zeros if (StrStr(pString, pDecimalMarkStr) != NULL) { pLastChar = pString + (StrLen(pString) * sizeof(*pString)) - 1; while (*pLastChar == L'0') { *pLastChar = L'\0'; pLastChar--; } } ppStringElements = StrSplit(pString, pDecimalMarkStr[0], &ElementsCount); if (ppStringElements == NULL || ElementsCount == 0 || ElementsCount > 2) { goto Finish; } for (Index = 0; Index < ElementsCount; Index++) { Valid = GetU64FromString(ppStringElements[Index], &DecimalElements[Index]); if (!Valid) { goto Finish; } } Decimal = (double) DecimalElements[0]; Fractional = (double) DecimalElements[1]; if (ElementsCount == 2) { for (Index = 0; Index < StrLen(ppStringElements[1]); Index++) { Fractional = Fractional * 0.1; } } *pOutValue = Decimal + Fractional; ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pDecimalMarkStr); FreeStringArray(ppStringElements, ElementsCount); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Compare a PackageSparing capability, encryption, soft SKU capabilities and SKU mode types. @param[in] SkuInformation1 - first SkuInformation to compare @param[in] SkuInformation2 - second SkuInformation to compare @retval NVM_SUCCESS - if everything went fine @retval NVM_ERR_DIMM_SKU_MODE_MISMATCH - if mode conflict occurred @retval NVM_ERR_DIMM_SKU_SECURITY_MISMATCH - if security mode conflict occurred **/ NvmStatusCode SkuComparison( IN UINT32 SkuInformation1, IN UINT32 SkuInformation2 ) { NvmStatusCode StatusCode = NVM_SUCCESS; NVDIMM_ENTRY(); if ((SkuInformation1 & SKU_MODES_MASK) != (SkuInformation2 & SKU_MODES_MASK)) { StatusCode = NVM_ERR_DIMM_SKU_MODE_MISMATCH; goto Finish; } if ((SkuInformation1 & SKU_ENCRYPTION_MASK) != (SkuInformation2 & SKU_ENCRYPTION_MASK)) { StatusCode = NVM_ERR_DIMM_SKU_SECURITY_MISMATCH; goto Finish; } /** Everything went fine **/ Finish: NVDIMM_EXIT(); return StatusCode; } /** Check if SKU conflict occurred. Any mixed modes between DIMMs are prohibited on a platform. @param[in] pDimmInfo1 - first DIMM_INFO to compare SKU mode @param[in] pDimmInfo2 - second DIMM_INFO to compare SKU mode @param[out] pSkuModeMismatch - pointer to a BOOLEAN value that will represent result of comparison @retval - Appropriate CLI return code **/ EFI_STATUS IsSkuModeMismatch( IN DIMM_INFO *pDimmInfo1 OPTIONAL, IN DIMM_INFO *pDimmInfo2 OPTIONAL, OUT BOOLEAN *pSkuModeMismatch ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NvmStatusCode StatusCode = NVM_ERR_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pDimmInfo1 == NULL || pDimmInfo2 == NULL || pSkuModeMismatch == NULL) { goto Finish; } *pSkuModeMismatch = FALSE; StatusCode = SkuComparison(pDimmInfo1->SkuInformation, pDimmInfo2->SkuInformation); if (StatusCode != NVM_SUCCESS) { *pSkuModeMismatch = TRUE; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I(ReturnCode); return ReturnCode; } /** Convert type to string @param[in] MemoryType, integer define type @retval CLI string representation of memory type **/ CHAR16* MemoryTypeToStr( IN UINT8 MemoryType ) { CHAR16 *pTempStr = NULL; switch (MemoryType) { case MEMORYTYPE_DDR4: pTempStr = CatSPrint(NULL, FORMAT_STR, MEMORY_TYPE_DDR4_STR); break; case MEMORYTYPE_DCPM: pTempStr = CatSPrint(NULL, FORMAT_STR, MEMORY_TYPE_DCPM_STR); break; case MEMORYTYPE_DDR5: pTempStr = CatSPrint(NULL, FORMAT_STR, MEMORY_TYPE_DDR5_STR); break; default: pTempStr = CatSPrint(NULL, FORMAT_STR, MEMORY_TYPE_UNKNOWN_STR); break; } return pTempStr; } /** Sort Linked List by using Bubble Sort. @param[in, out] LIST HEAD to sort @param[in] Compare Pointer to function that is needed for items comparing. It should return: -1 if "first < second" 0 if "first == second" 1 if "first > second" @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS BubbleSortLinkedList( IN OUT LIST_ENTRY *pList, IN INT32 (*Compare) (VOID *first, VOID *second) ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; BOOLEAN Swapped = FALSE; LIST_ENTRY *pNodeCurrentEntry = NULL; LIST_ENTRY *pNodeNextEntry = NULL; NVDIMM_ENTRY(); if (IsListEmpty(pList) || Compare == NULL) { goto Finish; } do { Swapped = FALSE; pNodeCurrentEntry = pList->ForwardLink; pNodeNextEntry = pNodeCurrentEntry->ForwardLink; while (pNodeNextEntry != pList) { if (Compare(pNodeCurrentEntry, pNodeNextEntry) > 0) { LIST_ENTRY * EFIAPI SwapListEntries( IN OUT LIST_ENTRY *FirstEntry, IN OUT LIST_ENTRY *SecondEntry ); SwapListEntries(pNodeCurrentEntry, pNodeNextEntry); pNodeCurrentEntry = pNodeNextEntry; pNodeNextEntry = pNodeNextEntry->ForwardLink; Swapped = TRUE; } pNodeCurrentEntry = pNodeNextEntry; pNodeNextEntry = pNodeNextEntry->ForwardLink; } } while (Swapped); ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Sort an array by using Bubble Sort. @param[in, out] pArray Array to sort @param[in] Count Number of items in array @param[in] ItemSize Size of item in bytes @param[in] Compare Pointer to function that is needed for items comparing. It should return: -1 if "first < second" 0 if "first == second" 1 if "first > second" @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS BubbleSort( IN OUT VOID *pArray, IN UINT32 Count, IN UINT32 ItemSize, IN INT32 (*Compare) (VOID *first, VOID *second) ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; BOOLEAN Swapped = FALSE; UINT32 Index = 0; VOID *pTmpItem = NULL; UINT8 *pFirst = NULL; UINT8 *pSecond = NULL; NVDIMM_ENTRY(); if (pArray == NULL || Compare == NULL) { goto Finish; } pTmpItem = AllocateZeroPool(ItemSize); if (pTmpItem == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } do { Swapped = FALSE; for (Index = 1; Index < Count; Index++) { pFirst = (UINT8 *) pArray + (Index - 1) * ItemSize; pSecond = (UINT8 *) pArray + Index * ItemSize; if (Compare(pFirst, pSecond) > 0) { CopyMem_S(pTmpItem, ItemSize, pFirst, ItemSize); CopyMem_S(pFirst, ItemSize, pSecond, ItemSize); CopyMem_S(pSecond, ItemSize, pTmpItem, ItemSize); Swapped = TRUE; } } } while (Swapped); ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pTmpItem); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Populates the units string based on the particular capacity unit @param[in] pData A pointer to the main HII data structure @param[in] Units The input unit to be converted into its HII string @param[out] ppUnitsStr A pointer to the HII units string. Dynamically allocated memory and must be released by calling function. @retval EFI_OUT_OF_RESOURCES if there is no space available to allocate memory for units string @retval EFI_INVALID_PARAMETER if one or more input parameters are invalid @retval EFI_SUCCESS The conversion was successful **/ EFI_STATUS UnitsToStr ( IN EFI_HII_HANDLE HiiHandle, IN UINT16 Units, OUT CHAR16 **ppUnitsStr ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (ppUnitsStr == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } switch (Units) { case DISPLAY_SIZE_UNIT_B: *ppUnitsStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_CAPACITY_UNIT_B), NULL); break; case DISPLAY_SIZE_UNIT_MB: *ppUnitsStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_CAPACITY_UNIT_MB), NULL); break; case DISPLAY_SIZE_UNIT_MIB: *ppUnitsStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_CAPACITY_UNIT_MIB), NULL); break; case DISPLAY_SIZE_UNIT_GB: *ppUnitsStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_CAPACITY_UNIT_GB), NULL); break; case DISPLAY_SIZE_UNIT_GIB: *ppUnitsStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_CAPACITY_UNIT_GIB), NULL); break; case DISPLAY_SIZE_UNIT_TB: *ppUnitsStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_CAPACITY_UNIT_TB), NULL); break; case DISPLAY_SIZE_UNIT_TIB: *ppUnitsStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_CAPACITY_UNIT_TIB), NULL); break; default: ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("Invalid units type!"); goto Finish; } if (*ppUnitsStr == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Convert last firmware update status to string. The caller function is obligated to free memory of the returned string. @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] Last Firmware update status value to convert @retval output string or NULL if memory allocation failed **/ CHAR16 * LastFwUpdateStatusToString( IN EFI_HANDLE HiiHandle, IN UINT8 LastFwUpdateStatus ) { CHAR16 *pLastFwUpdateStatusString = NULL; CHAR16 *pTempStr = NULL; switch (LastFwUpdateStatus) { case FW_UPDATE_STATUS_STAGED_SUCCESS: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_FW_UPDATE_STATUS_STAGED), NULL); pLastFwUpdateStatusString = CatSPrintClean(pLastFwUpdateStatusString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case FW_UPDATE_STATUS_LOAD_SUCCESS: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_FW_UPDATE_STATUS_SUCCESS), NULL); pLastFwUpdateStatusString = CatSPrintClean(pLastFwUpdateStatusString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case FW_UPDATE_STATUS_FAILED: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_FW_UPDATE_STATUS_FAIL), NULL); pLastFwUpdateStatusString = CatSPrintClean(pLastFwUpdateStatusString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; default: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_FW_UPDATE_STATUS_UNKNOWN), NULL); pLastFwUpdateStatusString = CatSPrintClean(pLastFwUpdateStatusString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; } return pLastFwUpdateStatusString; } /** Convert Quiesce required to string. The caller function is obligated to free memory of the returned string. @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] Quiesce required value to convert @retval output string or NULL if memory allocation failed **/ CHAR16 * QuiesceRequiredToString( IN EFI_HANDLE HiiHandle, IN UINT8 QuiesceRequired ) { CHAR16 *pQuiesceRequiredString = NULL; CHAR16 *pTempStr = NULL; switch (QuiesceRequired) { case QUIESCE_NOT_REQUIRED: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_QUIESCE_NOT_REQUIRED), NULL); pQuiesceRequiredString = CatSPrintClean(pQuiesceRequiredString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case QUIESCE_REQUIRED: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_QUIESCE_REQUIRED), NULL); pQuiesceRequiredString = CatSPrintClean(pQuiesceRequiredString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; default: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_UNKNOWN), NULL); pQuiesceRequiredString = CatSPrintClean(pQuiesceRequiredString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; } return pQuiesceRequiredString; } /** Convert StagedFwActivatable to string. The caller function is obligated to free memory of the returned string. @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] Staged Fw activatable value to convert @retval output string or NULL if memory allocation failed **/ CHAR16 * StagedFwActivatableToString( IN EFI_HANDLE HiiHandle, IN UINT8 StagedFwActivatable ) { CHAR16 *pStagedFwActivatableString = NULL; CHAR16 *pTempStr = NULL; switch (StagedFwActivatable) { case STAGED_FW_NOT_ACTIVATABLE: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STAGED_FW_NOT_ACTIVATABLE), NULL); pStagedFwActivatableString = CatSPrintClean(pStagedFwActivatableString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case STAGED_FW_ACTIVATABLE: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STAGED_FW_ACTIVATABLE), NULL); pStagedFwActivatableString = CatSPrintClean(pStagedFwActivatableString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; default: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_UNKNOWN), NULL); pStagedFwActivatableString = CatSPrintClean(pStagedFwActivatableString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; } return pStagedFwActivatableString; } /** Determines if an array, whose size is known in bytes has all elements as zero @param[in] pArray Pointer to the input array @param[in] ArraySize Array size in bytes @param[out] pAllElementsZero Pointer to a boolean that stores the result whether all array elements are zero @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ EFI_STATUS AllElementsInArrayZero( IN OUT VOID *pArray, IN UINT32 ArraySize, OUT BOOLEAN *pAllElementsZero ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR8 *pTempArray = (CHAR8 *)pArray; UINT32 Index = 0; BOOLEAN TempAllElementsZero = TRUE; NVDIMM_ENTRY(); if (pArray == NULL|| pAllElementsZero == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } for (Index = 0; Index < ArraySize; Index++) { if (pTempArray[Index] != 0) { TempAllElementsZero = FALSE; break; } } *pAllElementsZero = TempAllElementsZero; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Endian swap a uint32 value @param[in] OrigVal Value to modify @retval Value with the endian swap **/ UINT32 EndianSwapUint32( IN UINT32 OrigVal ) { UINT32 NewVal; NewVal = ((OrigVal & 0x000000FF) << 24) | ((OrigVal & 0x0000FF00) << 8) | ((OrigVal & 0x00FF0000) >> 8) | ((OrigVal & 0xFF000000) >> 24); return NewVal; } /** Endian swap a uint16 value @param[in] OrigVal Value to modify @retval Value with the endian swap **/ UINT16 EndianSwapUint16( IN UINT16 OrigVal ) { UINT16 NewVal; NewVal = ((OrigVal & 0x00FF) << 8) | ((OrigVal & 0xFF00) >> 8); return NewVal; } /** Converts EPOCH time in number of seconds into a human readable time string @param[in] TimeInSeconds Number of seconds (EPOCH time) @retval Human readable time string **/ CHAR16 *GetTimeFormatString (UINT64 TimeInSeconds, BOOLEAN verbose ) { int TimeSeconds = 0, TimeMinutes = 0, TimeHours = 0, TimeMonth = 0, TimeMonthDay = 0, TimeYear = 0, TimeWeekday = 0; UINT64 PartialDayInSeconds = 0; int NumberOfFullDays = 0; int CENTURY_MARKER = 1900; // EPOCH year century int EPOCH_YEAR_START = 1970; // EPOCH start = Thu Jan 1 1970 00:00:00 int WEEKDAY_OFFSET_FROM_EPOCH_START = 4; int SECONDS_PER_MINUTE = 60; int SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE; UINT64 SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR; int DaysPerMonth[2][12] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, // days per month in regular years { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } // days per month in leap year }; int Year = EPOCH_YEAR_START; CHAR16 *pTimeFormatString = NULL; const CHAR16 *DayOfWeek[] = { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" }; const CHAR16 *Month[] = { L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec" }; PartialDayInSeconds = (UINT64) TimeInSeconds % SECONDS_PER_DAY; NumberOfFullDays = (int)(TimeInSeconds / SECONDS_PER_DAY); TimeSeconds = PartialDayInSeconds % SECONDS_PER_MINUTE; TimeMinutes = (PartialDayInSeconds % SECONDS_PER_HOUR) / 60; TimeHours = (int)(PartialDayInSeconds / SECONDS_PER_HOUR); TimeWeekday = (NumberOfFullDays + WEEKDAY_OFFSET_FROM_EPOCH_START) % 7; while (NumberOfFullDays >= DAYS_IN_YEAR(Year)) { NumberOfFullDays -= DAYS_IN_YEAR(Year); Year++; } TimeYear = Year - CENTURY_MARKER; TimeMonth = 0; while (NumberOfFullDays >= DaysPerMonth[IS_LEAP_YEAR(Year)][TimeMonth]) { NumberOfFullDays -= DaysPerMonth[IS_LEAP_YEAR(Year)][TimeMonth]; TimeMonth++; } TimeMonthDay = NumberOfFullDays + 1; switch (verbose) { case TRUE: pTimeFormatString = CatSPrintClean(pTimeFormatString, FORMAT_STR_SPACE FORMAT_STR L" %02d %02d:%02d:%02d UTC %d", DayOfWeek[TimeWeekday], Month[TimeMonth], TimeMonthDay, TimeHours, TimeMinutes, TimeSeconds, TimeYear + CENTURY_MARKER ); // With verbose TRUE, timestamp looks like "Thu Jan 01 00:03:30 UTC 1998" break; default: pTimeFormatString = CatSPrintClean(pTimeFormatString, L"%02d/%02d/%d %02d:%02d:%02d", ++TimeMonth, TimeMonthDay, TimeYear + CENTURY_MARKER, TimeHours, TimeMinutes, TimeSeconds ); // With Default verbose, timestamp looks like "12/03/2018 14:55:21" break; } return pTimeFormatString; } /** Convert goal status bitmask to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] Status bits that define the goal status @retval CLI/HII string representation of goal status **/ CHAR16* GoalStatusToString( IN EFI_HANDLE HiiHandle, IN UINT8 Status ) { CHAR16 *pGoalStatusString = NULL; CHAR16 *pTempStr = NULL; if (HiiHandle == NULL) { return NULL; } switch (Status) { case GOAL_CONFIG_STATUS_UNKNOWN: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_PROVISIONING_FORM_GOAL_STATUS_UNKNOWN), NULL); pGoalStatusString = CatSPrintClean(pGoalStatusString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case GOAL_CONFIG_STATUS_NEW: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_PROVISIONING_FORM_GOAL_STATUS_REBOOT_REQUIRED), NULL); pGoalStatusString = CatSPrintClean(pGoalStatusString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case GOAL_CONFIG_STATUS_BAD_REQUEST: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_PROVISIONING_FORM_GOAL_STATUS_INVALID_GOAL), NULL); pGoalStatusString = CatSPrintClean(pGoalStatusString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case GOAL_CONFIG_STATUS_NOT_ENOUGH_RESOURCES: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_PROVISIONING_FORM_GOAL_STATUS_NOT_ENOUGH_RESOURCES), NULL); pGoalStatusString = CatSPrintClean(pGoalStatusString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; case GOAL_CONFIG_STATUS_FIRMWARE_ERROR: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_PROVISIONING_FORM_GOAL_STATUS_FIRMWARE_ERROR), NULL); pGoalStatusString = CatSPrintClean(pGoalStatusString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; default: pTempStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_PROVISIONING_FORM_GOAL_STATUS_UNKNOWN_ERROR), NULL); pGoalStatusString = CatSPrintClean(pGoalStatusString, FORMAT_STR, pTempStr); FREE_POOL_SAFE(pTempStr); break; } return pGoalStatusString; } EFI_STATUS GetNSLabelMajorMinorVersion( IN UINT32 NamespaceLabelVersion, OUT UINT16 *pMajor, OUT UINT16 *pMinor ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pMajor == NULL || pMinor == NULL) { goto Finish; } if ((NamespaceLabelVersion == NS_LABEL_VERSION_LATEST) || (NamespaceLabelVersion == NS_LABEL_VERSION_1_2)) { *pMajor = NSINDEX_MAJOR; *pMinor = NSINDEX_MINOR_2; ReturnCode = EFI_SUCCESS; } else if (NamespaceLabelVersion == NS_LABEL_VERSION_1_1) { *pMajor = NSINDEX_MAJOR; *pMinor = NSINDEX_MINOR_1; ReturnCode = EFI_SUCCESS; } else { NVDIMM_DBG("Invalid NamespaceLabelVersion: %d", NamespaceLabelVersion); } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Copies a source buffer to a destination buffer, and returns the destination buffer. @param DestinationBuffer The pointer to the destination buffer of the memory copy. @param DestLength The length in bytes of DestinationBuffer. @param SourceBuffer The pointer to the source buffer of the memory copy. @param Length The number of bytes to copy from SourceBuffer to DestinationBuffer. @return DestinationBuffer. **/ VOID * CopyMem_S( OUT VOID *DestinationBuffer, IN UINTN DestLength, IN CONST VOID *SourceBuffer, IN UINTN Length ) { #ifdef OS_BUILD int status = os_memcpy(DestinationBuffer, DestLength, SourceBuffer, Length); if(status != 0) { NVDIMM_CRIT("0x%x, 0x%x, 0x%x, 0x%x, 0x%x", DestinationBuffer, DestLength, SourceBuffer, Length, status); NVDIMM_CRIT("os_memcpy failed with ErrorCode: %x", status); } return DestinationBuffer; #else return CopyMem(DestinationBuffer, SourceBuffer, Length); #endif } /** Get manageability state for Dimm @param[in] SubsystemVendorId the SubsystemVendorId @param[in] interfaceCodeNum the number of interface codes @param[in] interfaceCodes the interface codes @param[in] SubsystemDeviceId the subsystem device ID @param[in] fwMajor the fw major version @param[in] fwMinor the fw minor version @retval BOOLEAN whether or not dimm is manageable **/ BOOLEAN IsDimmManageableByValues( IN UINT16 SubsystemVendorId, IN UINT32 interfaceCodeNum, IN UINT16* interfaceCodes, IN UINT16 SubsystemDeviceId, IN UINT8 fwMajor, IN UINT8 fwMinor ) { BOOLEAN Manageable = FALSE; if (IsDimmInterfaceCodeSupportedByValues(interfaceCodeNum, interfaceCodes) && (SPD_INTEL_VENDOR_ID == SubsystemVendorId) && IsSubsystemDeviceIdSupportedByValues(SubsystemDeviceId) && IsFwApiVersionSupportedByValues(fwMajor, fwMinor)) { Manageable = TRUE; } return Manageable; } /** Check if the dimm interface code of this DIMM is supported @param[in] interfaceCodeNum the number of interface codes @param[in] interfaceCodes the interface codes @retval true if supported, false otherwise **/ BOOLEAN IsDimmInterfaceCodeSupportedByValues( IN UINT32 interfaceCodeNum, IN UINT16* interfaceCodes ) { BOOLEAN Supported = FALSE; UINT32 Index = 0; if (interfaceCodes != NULL) { for (Index = 0; Index < interfaceCodeNum; Index++) { if (DCPMM_FMT_CODE_APP_DIRECT == interfaceCodes[Index]) { Supported = TRUE; break; } } if (!Supported) { NVDIMM_ERR("Supported Interface Format Code not found!"); } } return Supported; } /** Check if the subsystem device ID of this DIMM is supported @param[in] SubsystemDeviceId the subsystem device ID @retval true if supported, false otherwise **/ BOOLEAN IsSubsystemDeviceIdSupportedByValues( IN UINT16 SubsystemDeviceId ) { BOOLEAN Supported = FALSE; if ((SubsystemDeviceId >= SPD_DEVICE_ID_10) && (SubsystemDeviceId <= SPD_DEVICE_ID_20)) { Supported = TRUE; } return Supported; } /** Check if current firmware API version is supported @param[in] major the major version @param[in] minor the minor version @retval true if supported, false otherwise **/ BOOLEAN IsFwApiVersionSupportedByValues( IN UINT8 major, IN UINT8 minor ) { BOOLEAN VerSupported = TRUE; if (((major < MIN_FIS_SUPPORTED_BY_THIS_SW_MAJOR) || (major == MIN_FIS_SUPPORTED_BY_THIS_SW_MAJOR && minor < MIN_FIS_SUPPORTED_BY_THIS_SW_MINOR)) || (major > MAX_FIS_SUPPORTED_BY_THIS_SW_MAJOR)) { VerSupported = FALSE; } return VerSupported; } /** Convert controller revision id to string @param[in] Controller revision id @param[in] Subsystem Device Id for determining HW Gen @retval CLI string representation of the controller revision id **/ CHAR16* ControllerRidToStr( IN UINT16 ControllerRid, IN UINT16 SubsystemDeviceId ) { CHAR16* BaseStepArrayGen100_Gen200[] = { CONTROLLER_REVISION_A_STEP_STR, CONTROLLER_REVISION_S_STEP_STR, CONTROLLER_REVISION_B_STEP_STR, CONTROLLER_REVISION_C_STEP_STR }; CHAR16* BaseStepArrayGen300[] = { CONTROLLER_REVISION_A_STEP_STR, CONTROLLER_REVISION_B_STEP_STR, CONTROLLER_REVISION_C_STEP_STR, CONTROLLER_REVISION_D_STEP_STR }; CHAR16* pSteppingStr = NULL; UINT8 BaseStep = 0; UINT8 MetalStep = 0; NVDIMM_ENTRY(); BaseStep = (ControllerRid & CONTROLLER_REVISION_BASE_STEP_MASK) >> 4; /* mask and shift to get BaseStep as 0-3 */ MetalStep = ControllerRid & CONTROLLER_REVISION_METAL_STEP_MASK; if (SubsystemDeviceId >= SPD_DEVICE_ID_20) { pSteppingStr = CatSPrintClean(NULL, FORMAT_STEPPING, BaseStepArrayGen300[BaseStep], MetalStep, ControllerRid); } else { pSteppingStr = CatSPrintClean(NULL, FORMAT_STEPPING, BaseStepArrayGen100_Gen200[BaseStep], MetalStep, ControllerRid); } NVDIMM_EXIT(); return pSteppingStr; } /** Convert FIPS mode status to string @param[in] HiiHandle HII handle to access string dictionary @param[in] FIPSMode Response from GetFIPSMode firmware command @param[in] FwVer Firmware revision @param[in] ReturnCodeGetFIPSMode ReturnCode from GetFIPSMode API call, used to provide clearer error message @retval String representation of the FIPS mode status **/ CHAR16 * ConvertFIPSModeToString( IN EFI_HANDLE HiiHandle, IN FIPS_MODE FIPSMode, IN FIRMWARE_VERSION FwVer, IN EFI_STATUS ReturnCodeGetFIPSMode ) { CHAR16 *pFIPSModeStatusStr = NULL; if((FwVer.FwApiMajor < 3 ) || (FwVer.FwApiMajor == 3 && FwVer.FwApiMinor < 5)) { // FIPS is only supported with >= 3.5. Return N/A pFIPSModeStatusStr = CatSPrint(NULL, FORMAT_STR, NOT_APPLICABLE_SHORT_STR); goto Finish; } if (EFI_ERROR(ReturnCodeGetFIPSMode)) { // If for some reason the FIPS call failed on a newer firmware, let's put unknown // instead of N/A pFIPSModeStatusStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_UNKNOWN), NULL); } switch (FIPSMode.Status) { case FIPSModeStatusNonFIPSMode: pFIPSModeStatusStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_FIPS_MODE_STATUS_NON_FIPS_MODE), NULL); break; case FIPSModeStatusNonFIPSModeUntilNextBoot: pFIPSModeStatusStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_FIPS_MODE_STATUS_NON_FIPS_MODE_UNTIL_NEXT_BOOT), NULL); break; case FIPSModeStatusInitializationNotDone: pFIPSModeStatusStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_FIPS_MODE_STATUS_INITIALIZATION_NOT_DONE), NULL); break; case FIPSModeStatusInitializationDone: pFIPSModeStatusStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_FIPS_MODE_STATUS_INITIALIZATION_DONE), NULL); break; default: pFIPSModeStatusStr = HiiGetString(HiiHandle, STRING_TOKEN(STR_DCPMM_STATUS_ERR_UNKNOWN), NULL); break; } Finish: return pFIPSModeStatusStr; } /** Set object status for DIMM_INFO @param[out] pCommandStatus Pointer to command status structure @param[in] pDimm DIMM_INFO for which the object status is being set @param[in] Status Object status to set **/ VOID SetObjStatusForDimmInfo( OUT COMMAND_STATUS *pCommandStatus, IN DIMM_INFO *pDimm, IN NVM_STATUS Status ) { SetObjStatusForDimmInfoWithErase(pCommandStatus, pDimm, Status, FALSE); } /** Set object status for DIMM_INFO @param[out] pCommandStatus Pointer to command status structure @param[in] pDimm DIMM_INFO for which the object status is being set @param[in] Status Object status to set @param[in] If TRUE - clear all other status before setting this one **/ VOID SetObjStatusForDimmInfoWithErase( OUT COMMAND_STATUS *pCommandStatus, IN DIMM_INFO *pDimm, IN NVM_STATUS Status, IN BOOLEAN EraseFirst ) { UINT32 idx = 0; CHAR16 DimmUid[MAX_DIMM_UID_LENGTH]; CHAR16 *TmpDimmUid = NULL; if (pDimm == NULL || pCommandStatus == NULL) { return; } for (idx = 0; idx < MAX_DIMM_UID_LENGTH; idx++) { DimmUid[idx] = 0; } if (pDimm->VendorId != 0 && pDimm->ManufacturingInfoValid != FALSE && pDimm->SerialNumber != 0) { TmpDimmUid = CatSPrint(NULL, L"%04x", EndianSwapUint16(pDimm->VendorId)); if (pDimm->ManufacturingInfoValid == TRUE) { TmpDimmUid = CatSPrintClean(TmpDimmUid, L"-%02x-%04x", pDimm->ManufacturingLocation, EndianSwapUint16(pDimm->ManufacturingDate)); } TmpDimmUid = CatSPrintClean(TmpDimmUid, L"-%08x", EndianSwapUint32(pDimm->SerialNumber)); } else { TmpDimmUid = CatSPrint(NULL, L""); } if (TmpDimmUid != NULL) { StrnCpyS(DimmUid, MAX_DIMM_UID_LENGTH, TmpDimmUid, MAX_DIMM_UID_LENGTH - 1); FREE_POOL_SAFE(TmpDimmUid); } if (EraseFirst) { EraseObjStatus(pCommandStatus, pDimm->DimmHandle, DimmUid, MAX_DIMM_UID_LENGTH, ObjectTypeDimm); } SetObjStatus(pCommandStatus, pDimm->DimmHandle, DimmUid, MAX_DIMM_UID_LENGTH, Status, ObjectTypeDimm); } /** Retrieve the number of bits set in a number Based on Brian Kernighan's Algorithm @param[in] Number Number in which number of bits set is to be counted @param[out] pNumOfBitsSet Number of bits set **/ EFI_STATUS CountNumOfBitsSet( IN UINT64 Number, OUT UINT8 *pNumOfBitsSet ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pNumOfBitsSet == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pNumOfBitsSet = 0; while (Number) { Number &= (Number - 1); (*pNumOfBitsSet)++; } Finish: NVDIMM_EXIT(); return ReturnCode; } /** Retrieve the bitmap for NumOfChannelWays @param[in] NumOfChannelWays Number of ChannelWays or Number of Dimms used in an Interleave Set @param[out] pBitField Bitmap based on PCAT 2.0 Type 1 Table for ChannelWays **/ EFI_STATUS GetBitFieldForNumOfChannelWays( IN UINT64 NumOfChannelWays, OUT UINT16 *pBitField ) { EFI_STATUS ReturnCode = EFI_SUCCESS; if (pBitField == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } switch (NumOfChannelWays) { case 1: *pBitField = INTERLEAVE_SET_1_WAY; break; case 2: *pBitField = INTERLEAVE_SET_2_WAY; break; case 3: *pBitField = INTERLEAVE_SET_3_WAY; break; case 4: *pBitField = INTERLEAVE_SET_4_WAY; break; case 6: *pBitField = INTERLEAVE_SET_6_WAY; break; case 8: *pBitField = INTERLEAVE_SET_8_WAY; break; case 12: *pBitField = INTERLEAVE_SET_12_WAY; break; case 16: *pBitField = INTERLEAVE_SET_16_WAY; break; case 24: *pBitField = INTERLEAVE_SET_24_WAY; break; default: NVDIMM_WARN("Unsupported number of channel ways: %d", NumOfChannelWays); *pBitField = 0; break; } Finish: NVDIMM_EXIT(); return ReturnCode; } /** Converts a DIMM_INFO_ATTRIB_X attribute to a string @param[in] pAttrib - a DIMM_INFO_ATTRIB_X attribute to convert @param[in] pFormatStr - optional format string to use for conversion **/ CHAR16 * ConvertDimmInfoAttribToString( IN VOID *pAttrib, IN CHAR16* pFormatStr OPTIONAL) { DIMM_INFO_ATTRIB_HEADER *pHeader = (DIMM_INFO_ATTRIB_HEADER *)pAttrib; if (NULL == pAttrib) { return NULL; } if (pHeader->Status.Code == EFI_UNSUPPORTED) { return NULL; } if (pHeader->Status.Code) { return CatSPrintClean(NULL, L"Unknown"); } switch (pHeader->Type) { case DIMM_INFO_TYPE_BOOLEAN: if (pFormatStr) { return CatSPrintClean(NULL, pFormatStr, ((DIMM_INFO_ATTRIB_BOOLEAN *)pAttrib)->Data); } else if(((DIMM_INFO_ATTRIB_BOOLEAN *)pAttrib)->Data){ return CatSPrintClean(NULL, L"TRUE"); } else { return CatSPrintClean(NULL, L"FALSE"); } case DIMM_INFO_TYPE_CHAR16: return (NULL == pFormatStr) ? CatSPrintClean(NULL, FORMAT_STR, ((DIMM_INFO_ATTRIB_CHAR16 *)pAttrib)->Data) : CatSPrintClean(NULL, pFormatStr, ((DIMM_INFO_ATTRIB_CHAR16 *)pAttrib)->Data); case DIMM_INFO_TYPE_UINT8: return (NULL == pFormatStr) ? CatSPrintClean(NULL, L"%d", ((DIMM_INFO_ATTRIB_UINT8 *)pAttrib)->Data) : CatSPrintClean(NULL, pFormatStr, ((DIMM_INFO_ATTRIB_UINT8 *)pAttrib)->Data); case DIMM_INFO_TYPE_UINT16: return (NULL == pFormatStr) ? CatSPrintClean(NULL, L"%d", ((DIMM_INFO_ATTRIB_UINT16 *)pAttrib)->Data) : CatSPrintClean(NULL, pFormatStr, ((DIMM_INFO_ATTRIB_UINT16 *)pAttrib)->Data); case DIMM_INFO_TYPE_UINT32: return (NULL == pFormatStr) ? CatSPrintClean(NULL, L"%d", ((DIMM_INFO_ATTRIB_UINT32 *)pAttrib)->Data) : CatSPrintClean(NULL, pFormatStr, ((DIMM_INFO_ATTRIB_UINT32 *)pAttrib)->Data); } return NULL; } /** Guess an appropriate NVM_STATUS code from EFI_STATUS. For use when pCommandStatus is not an argument to a lower level function. Used currently to get specific errors relevant to the user out to the CLI but not many (especially lower-level) functions have pCommandStatus. Also the CLI printer doesn't use ReturnCode, only pCommandStatus. @param[in] ReturnCode - EFI_STATUS returned from function call @retval - Appropriate guess at the NVM_STATUS code **/ NVM_STATUS GuessNvmStatusFromReturnCode( IN EFI_STATUS ReturnCode ) { NVM_STATUS NvmStatus = NVM_ERR_OPERATION_NOT_STARTED; switch (ReturnCode) { case EFI_DEVICE_ERROR: NvmStatus = NVM_ERR_DEVICE_ERROR; break; case EFI_INCOMPATIBLE_VERSION: NvmStatus = NVM_ERR_INCOMPATIBLE_SOFTWARE_REVISION; break; case EFI_VOLUME_CORRUPTED: NvmStatus = NVM_ERR_DATA_TRANSFER; break; case EFI_NO_RESPONSE: NvmStatus = NVM_ERR_BUSY_DEVICE; break; case EFI_NOT_FOUND: NvmStatus = NVM_ERR_INIT_FAILED_NO_MODULES_FOUND; break; // Don't have a default state for now, keep default "not started" error } return NvmStatus; } /** Create a duplicate of a string without parsing any format strings. Caller is responsible for freeing the returned string @param[in] StringToDuplicate - String to duplicate @param[out] pDuplicateString - Allocated copy of StringToDuplicate **/ EFI_STATUS DuplicateString( IN CHAR16 *StringToDuplicate, OUT CHAR16 **pDuplicateString ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 StringLength; CHECK_NULL_ARG(StringToDuplicate, Finish); CHECK_NULL_ARG(pDuplicateString, Finish); // Only support up to MAX_STRING_LENGTH-1 StringLength = (UINT32)StrnLenS(StringToDuplicate, MAX_STRING_LENGTH); CHECK_NOT_TRUE(StringLength <= (MAX_STRING_LENGTH-1), Finish); // Make a copy of the string // +1 for null char CHECK_RESULT_MALLOC(*pDuplicateString, AllocateZeroPool(sizeof(CHAR16)*(StringLength+1)), Finish); // Max destination size is also StringLength+1 since that's all we allocated CHECK_RESULT(StrnCpyS(*pDuplicateString, StringLength+1, StringToDuplicate, StringLength+1), Finish); Finish: if (EFI_ERROR(ReturnCode) && NULL != pDuplicateString) { FREE_POOL_SAFE(*pDuplicateString); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Wrap the string (add \n) at the specified WrapPos by replacing a space character (' ') with a newline character. Used for the HII popup window. Make a copy of the MessageString so we can modify it if needed. @param[in] WrapPos - Line length limit (inclusive). Does not include "\n" or "\0" @param[in] MessageString - Original message string, is not modified @param[out] pWrappedString - Allocated copy of MessageString that is wrapped with "\n" **/ EFI_STATUS WrapString( IN UINT8 WrapPos, IN CHAR16 *MessageString, OUT CHAR16 **pWrappedString ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINTN StringIndex = 0; // Index in the current line. Should not be > WrapPos UINTN LineIndex = 0; UINTN SpaceIndex = 0; BOOLEAN NewlineCharFoundOrAdded = FALSE; UINT32 MessageStringLength; CHAR16 Char; CHECK_NULL_ARG(MessageString, Finish); CHECK_NULL_ARG(pWrappedString, Finish); CHECK_RESULT(DuplicateString(MessageString, pWrappedString), Finish); MessageStringLength = (UINT32)StrnLenS(*pWrappedString, MAX_STRING_LENGTH); // Check if nothing to do if (MessageStringLength <= WrapPos) { ReturnCode = EFI_SUCCESS; goto Finish; } // Use magic characters instead of #def's in FormatStrings.h (FORMAT_NL) // because apparently L"\n" != L'\n' and this code is UEFI HII specific, so // don't really need it for other uses. while (StringIndex < MessageStringLength) { // Make it a little bit easier to debug Char = (*pWrappedString)[StringIndex]; if (Char == L' ') { SpaceIndex = StringIndex; } else if (Char == L'\n') { // Already a newline inserted, just reset the counters (at the end) NewlineCharFoundOrAdded = TRUE; } // Wrap-around case. Convert zero-indexed to one-indexed (WrapPos) by adding one if (LineIndex+1 > WrapPos && NewlineCharFoundOrAdded == FALSE) { if (SpaceIndex != 0) { // Replace space with \n (*pWrappedString)[SpaceIndex] = L'\n'; NewlineCharFoundOrAdded = TRUE; // Count the next string starting at the added newline, not at the current // LineIndex StringIndex = SpaceIndex; } else { NVDIMM_DBG("No spaces or dashes found in popup string...weird! Not inserting newline to highlight the issue"); } } // Only increment line index in non-newline scenarios. // (If the current character is a "\n", we want the next character's LineIndex value // to be 0 and not 1) if (NewlineCharFoundOrAdded == TRUE) { SpaceIndex = 0; LineIndex = 0; NewlineCharFoundOrAdded = FALSE; } else { LineIndex++; } // Always increment StringIndex StringIndex++; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/common/Utility.h000066400000000000000000001764211440615110200202570ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _UTILITY_H_ #define _UTILITY_H_ #include #include #include "NvmInterface.h" #include "NvmHealth.h" #ifdef _MSC_VER int _fltused(); #endif #define EFI_FILE_MODE_BINARY 0x8000000000000064ULL /** The Config Protocol version bytes definition **/ #pragma pack(push, 1) typedef struct { UINT32 Major : 16; UINT32 Minor : 15; UINT32 BuildType : 1; } CpVersionSeparated; #pragma pack(pop) typedef union { UINT32 AsUint32; CpVersionSeparated Separated; } CONFIG_PROTOCOL_VERSION; #define SOCKET_ID_ALL MAX_UINT16 #define BLOCKSIZE_4K 4096 #define SPD_INTEL_VENDOR_ID 0x8980 #define SPD_DEVICE_ID 0x0000 #define SPD_DEVICE_ID_10 0x097A #define SPD_DEVICE_ID_15 0x097B #define SPD_DEVICE_ID_20 0x097C #define CONTROLLER_REVISION_BASE_STEP_MASK 0x30 #define CONTROLLER_REVISION_METAL_STEP_MASK 0x03 #define CONTROLLER_REVISION_A_STEP_STR L"A" #define CONTROLLER_REVISION_S_STEP_STR L"S" #define CONTROLLER_REVISION_B_STEP_STR L"B" #define CONTROLLER_REVISION_C_STEP_STR L"C" #define CONTROLLER_REVISION_D_STEP_STR L"D" #define CONTROLLER_STEPPING_UNKNOWN_STR L"Unknown stepping" #define MAX_CONFIG_DUMP_FILE_SIZE OUT_MB_SIZE // Picking a theoretical max string length so we don't run out of // memory and kill something that is continuously running (our driver). // Also it's good development practice. #define MAX_LINE_CHAR_LENGTH 400 #define MAX_LINE_BYTE_LENGTH (MAX_LINE_CHAR_LENGTH * sizeof(CHAR8)) #define MAX_STRING_LENGTH (16384) #define COUNT_TO_INDEX_OFFSET 1 #define FIRST_CHAR_INDEX 0 #define UTF_16_BOM L'\xFEFF' #define SKU_MEMORY_MODE_FLAG (BIT0) #define SKU_APP_DIRECT_MODE_FLAG (BIT2) #define SKU_MODES_MASK (SKU_MEMORY_MODE_FLAG | SKU_APP_DIRECT_MODE_FLAG) #define SKU_ENCRYPTION_MASK (BIT17) //Long operation timer defines #define LONG_OP_POLL_EVENT_TIMER 0 //timer used for polling #define LONG_OP_POLL_TIMER_INTERVAL EFI_TIMER_PERIOD_SECONDS(1) //defines how often to poll long op status #define LONG_OP_POLL_EVENT_TIMEOUT 1 //timer used for timeout event #define LONG_OP_POLL_EVENT_SIZE 2 //total number of events to wait for #define FW_UPDATE_TIMEOUT_SECONDS 20 #define LONG_OP_FW_UPDATE_TIMEOUT EFI_TIMER_PERIOD_SECONDS(FW_UPDATE_TIMEOUT_SECONDS) //Firmware update opcodes to match long operation status #define FW_UPDATE_OPCODE 0x09 #define FW_UPDATE_SUBOPCODE 0x00 // Product name string values #define PMEM_MODULE_STR L"PMem module" #define PMEM_MODULES_STR PMEM_MODULE_STR L"s" #define PMEM_MODULE_PASCAL_CASE_STR L"PMemModule" // Last shutdown status string values #define LAST_SHUTDOWN_STATUS_PM_ADR_STR L"PM ADR Command Received" #define LAST_SHUTDOWN_STATUS_PM_S3_STR L"PM S3 Received" #define LAST_SHUTDOWN_STATUS_PM_S5_STR L"PM S5 Received" #define LAST_SHUTDOWN_STATUS_DDRT_POWER_FAIL_STR L"DDRT Power Fail Command Received" #define LAST_SHUTDOWN_STATUS_PMIC_POWER_LOSS_STR L"PMIC 12V/DDRT 1.2V Power Loss (PLI)" #define LAST_SHUTDOWN_STATUS_PM_WARM_RESET_STR L"PM Warm Reset Received" #define LAST_SHUTDOWN_STATUS_THERMAL_SHUTDOWN_STR L"Thermal Shutdown Received" #define LAST_SHUTDOWN_STATUS_FW_FLUSH_COMPLETE_STR L"Controller's FW State Flush Complete" #define LAST_SHUTDOWN_STATUS_UNKNOWN_STR L"Unknown" // Last shutdown status extended string values #define LAST_SHUTDOWN_STATUS_VIRAL_INTERRUPT_STR L"Viral Interrupt Received" #define LAST_SHUTDOWN_STATUS_SURPRISE_CLOCK_STOP_INTERRUPT_STR L"Surprise Clock Stop Received" #define LAST_SHUTDOWN_STATUS_WRITE_DATA_FLUSH_COMPLETE_STR L"Write Data Flush Complete" #define LAST_SHUTDOWN_STATUS_S4_POWER_STATE_STR L"PM S4 Received" #define LAST_SHUTDOWN_STATUS_PM_IDLE_STR L"PM Idle Received" #define LAST_SHUTDOWN_STATUS_SRE_CLOCK_STOP_STR L"SRE Clock Stop Received" #define LAST_SHUTDOWN_STATUS_SURPRISE_RESET_STR L"DDRT Surprise Reset Received" #define LAST_SHUTDOWN_STATUS_ENHANCED_ADR_FLUSH_COMPLETE_STR L"Extended Flush Complete" #define LAST_SHUTDOWN_STATUS_ENHANCED_ADR_FLUSH_NOT_COMPLETE_STR L"Extended Flush Not Complete" #define LAST_SHUTDOWN_STATUS_ENHANCED_SX_EXTENDED_FLUSH_COMPLETE_STR L"Sx Extended Flush Complete" #define LAST_SHUTDOWN_STATUS_ENHANCED_SX_EXTENDED_FLUSH_NOT_COMPLETE_STR L"Sx Extended Flush Not Complete" // Memory modes supported string values #define MODES_SUPPORTED_MEMORY_MODE_STR L"Memory Mode" #define MODES_SUPPORTED_APP_DIRECT_MODE_STR L"App Direct" // Software triggers enabled string values #define SW_TRIGGERS_ENABLED_NONE_STR L"None" #define SW_TRIGGERS_ENABLED_BIT0_STR L"Package Sparing" #define SW_TRIGGERS_ENABLED_BIT1_STR L"Reserved" #define SW_TRIGGERS_ENABLED_BIT2_STR L"Fatal Error" #define SW_TRIGGERS_ENABLED_BIT3_STR L"Percentage Remaining" #define SW_TRIGGERS_ENABLED_BIT4_STR L"Dirty Shutdown" // Security capabilities string values #define SECURITY_CAPABILITIES_ENCRYPTION L"Encryption" #define SECURITY_CAPABILITIES_ERASE L"Erase" #define SECURITY_CAPABILITIES_NONE L"None" // Memory type string values #define MEMORY_TYPE_DCPM_STR L"Logical Non-Volatile Device" #define MEMORY_TYPE_DDR4_STR L"DDR4" #define MEMORY_TYPE_DDR5_STR L"DDR5" #define MEMORY_TYPE_UNKNOWN_STR L"Unknown" //Units string values #define UNITS_B_STR L"B" #define UNITS_GB_STR L"GB" #define UNITS_GIB_STR L"GiB" #define UNITS_MB_STR L"MB" #define UNITS_MIB_STR L"MiB" #define UNITS_TB_STR L"TB" #define UNITS_TIB_STR L"TiB" //Number of digits after point #define ONE_DIGIT_AFTER_POINT 1 #define TWO_DIGITS_AFTER_POINT 2 #define THREE_DIGITS_AFTER_POINT 3 //Namespace health states #define HEALTHSTATE_OK L"Healthy" #define HEALTHSTATE_WARNING L"Warning" #define HEALTHSTATE_CRITICAL L"Critical" #define HEALTHSTATE_UNKNOWN L"Unknown" #define HEALTHSTATE_UNSUPPORTED L"Unsupported" #define HEALTHSTATE_LOCKED L"Locked" //Persistent Memory Type #define PERSISTENT_MEM_TYPE_AD_STR L"AppDirect" #define PERSISTENT_MEM_TYPE_AD_NI_STR L"AppDirectNotInterleaved" // Interface Format Code #define FORMAT_CODE_APP_DIRECT_STR L"(Non-Energy Backed Byte Addressable)" /** We define the EFI Shell Protocol Guid locally, so that we won't include Shell Package headers in the driver. The #define name is slightly different than in the UDK, so that we will avoid symbol redefinitions. **/ #define EFI_SHELL_PROTOCOL_GUID \ { 0x6302d008, 0x7f9b, 0x4f30, { 0x87, 0xac, 0x60, 0xc9, 0xfe, 0xf5, 0xda, 0x4e } } #ifdef _MSC_VER # define INLINE __inline #elif defined(__GNUC__) # define INLINE __attribute__ ((gnu_inline)) inline #else # define INLINE inline #endif /** BCD - Binary coded decimals macros Macros are used to compact two digits into single 8bit variable and vice versa **/ #define BAD_POINTER ((VOID *) 0xAFAFAFAFAFAFAFAF) #define BCD_TO_TWO_DEC(BUFF) ((((BUFF) >> 4) * 10) + ((BUFF) & 0xF)) #define TWO_DEC_TO_BCD(A, B) (((A) << 4) + (B)) #define INTERVAL(MIN, MAX) L"<" DEFINE_TO_STRING(MIN) L"," DEFINE_TO_STRING(MAX) L">" #define INTERVAL_OPEN(MIN, MAX) L"(" DEFINE_TO_STRING(MIN) L"," DEFINE_TO_STRING(MAX) L")" #define BIT_ON(var, bit_number) {(var) |= (1 << (bit_number)); } #define BIT_OFF(var, bit_number) {(var) &= (~(1 << (bit_number))); } #define BIT_GET(var, bit_number) ((var) & (1 << bit_number)) #define IS_BIT_SET_VAR(var, bit_mask) (((var) & (bit_mask)) != 0) #define FIRST_POOL_GOAL 0 #define SECOND_POOL_GOAL 1 #define BYTES_TO_KIB(Size) ((Size)>>10) #define BYTES_TO_KB(Size) ((Size)/1000) #define BYTES_TO_MIB(Size) ((Size)>>20) #define BYTES_TO_MB(Size) ((Size)/1000/1000) #define BYTES_TO_GIB(Size) ((Size)>>30) #define BYTES_TO_TIB(Size) ((Size)>>40) #define BYTES_TO_TB(Size) ((Size)/1000/1000/1000/1000) #define BYTES_TO_GB(Size) ((Size)/1000/1000/1000) #define KIB_TO_BYTES(Size) ((Size) * BYTES_IN_KIB) #define MIB_TO_BYTES(Size) ((Size) * BYTES_IN_MEBIBYTE) #define MB_TO_BYTES(Size) ((Size) * BYTES_IN_MEGABYTE) #define GIB_TO_BYTES(Size) ((Size) * BYTES_IN_GIBIBYTE) #define GB_TO_BYTES(Size) ((Size) * BYTES_IN_GIGABYTE) #define TIB_TO_BYTES(Size) ((Size) * BYTES_IN_TEBIBYTE) #define TB_TO_BYTES(Size) ((Size) * BYTES_IN_TERABYTE) #define KIB_TO_MIB(Size) ((Size)>>10) #define GIB_TO_MIB(Size) ((Size)<<10) #define MIB_TO_GIB(Size) ((Size)>>10) #define BYTES_TO_TIB_DOUBLE(Size) (((double)Size)/((double)BYTES_IN_MEBIBYTE))/((double)BYTES_IN_MEBIBYTE) #define BYTES_TO_MIB_DOUBLE(Size) (((double)Size)/((double)BYTES_IN_MEBIBYTE)) #define BYTES_TO_GIB_DOUBLE(Size) (((double)Size)/((double)BYTES_IN_GIBIBYTE)) #define BYTES_TO_TB_DOUBLE(Size) (((double)Size)/((double)BYTES_IN_MEGABYTE))/((double)BYTES_IN_MEGABYTE) #define BYTES_TO_MB_DOUBLE(Size) (((double)Size)/((double)BYTES_IN_MEGABYTE)) #define BYTES_TO_GB_DOUBLE(Size) (((double)Size)/((double)BYTES_IN_GIGABYTE)) #define GIB_TO_GB(Size) (BYTES_TO_GB(GIB_TO_BYTES(Size))) #define GB_TO_GIB(Size) (BYTES_TO_GIB(GB_TO_BYTES(Size))) #define BYTES_IN_TERABYTE (BYTES_IN_GIGABYTE * BYTES_IN_KB) #define BYTES_IN_GIGABYTE (BYTES_IN_MEGABYTE * BYTES_IN_KB) #define BYTES_IN_MEGABYTE (BYTES_IN_KB * BYTES_IN_KB) #define BYTES_IN_KB (UINT64)(1000) #define BYTES_IN_TEBIBYTE (BYTES_IN_GIBIBYTE * BYTES_IN_KIB) #define BYTES_IN_GIBIBYTE (BYTES_IN_MEBIBYTE * BYTES_IN_KIB) #define BYTES_IN_MEBIBYTE (BYTES_IN_KIB * BYTES_IN_KIB) #define BYTES_IN_KIB (UINT64)(1 << 10) #define MEBIBYTES_IN_GIBIBYTE (1024) #define GET_VOID_PTR_OFFSET(ptr, n) ((VOID *) ((UINT8 *) (ptr) + (n))) #define IS_WHITE_UNICODE(Char) ((Char) == L' ' || (Char) == L'\t' || (Char) == L'\r' || (Char) == L'\n') #define IS_WHITE_ASCII(Char) ((Char) == ' ' || (Char) == '\t' || (Char) == '\r' || (Char) == '\n') #define IS_IN_RANGE(X, A, B) ((X) >= (A) && (X) <= (B)) #define FREE_HII_POINTER(pBuffer) { \ if ((VOID *) pBuffer != NULL) { \ FreePool((VOID*) pBuffer); \ pBuffer = (UINT64) NULL; \ } \ }; #define FREE_POOL_SAFE(pBuffer) { \ if (pBuffer != NULL) { \ FreePool((VOID *)pBuffer); \ pBuffer = NULL; \ } \ }; #define FREE_ALIGNED_POOL_SAFE(pBuffer, NumberOfPages) { \ if (pBuffer != NULL) { \ FreeAlignedPages((VOID *)pBuffer, NumberOfPages); \ pBuffer = NULL; \ } \ }; #define FREE_CMD_DISPLAY_OPTIONS_SAFE(pCmdOptions) { \ if (pCmdOptions != NULL) { \ if(pCmdOptions->pDisplayValues != NULL) { \ FreePool((VOID *)pCmdOptions->pDisplayValues); \ pCmdOptions->pDisplayValues = NULL; \ } \ FreePool((VOID *)pCmdOptions); \ pCmdOptions = NULL; \ } \ }; #ifdef OS_BUILD #ifdef _MSC_VER #define CHECK_WIN_ADMIN_PERMISSIONS() { \ if (NVM_SUCCESS != os_check_admin_permissions()) { \ PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_CMD_FAILED_NOT_ADMIN); \ ReturnCode = EFI_UNSUPPORTED; \ goto Finish; \ } \ }; #else // MSC_VER #define CHECK_WIN_ADMIN_PERMISSIONS() #endif // MSC_VER #else // OS_BUILD #define CHECK_WIN_ADMIN_PERMISSIONS() #endif // OS_BUILD /** Persist the first error encountered. @param[in,out] ReturnCode Return code to be returned if indicating an error @param[in] ReturnCodeNew Return code to be returned if ReturnCode does not indicate error **/ #define KEEP_ERROR(ReturnCode, ReturnCodeNew) \ ReturnCode = (ReturnCode > EFI_SUCCESS) ? ReturnCode : ReturnCodeNew; /** Linked lists iterators **/ #define LIST_FOR_EACH(Entry, ListHead) \ for (Entry = (ListHead)->ForwardLink; Entry != (ListHead); Entry = Entry->ForwardLink) #define LIST_FOR_EACH_REVERSE(Entry, ListHead) \ for (Entry = (ListHead)->BackLink; Entry != (ListHead); Entry = Entry->BackLink) #define LIST_FOR_EACH_SAFE(Entry, NextEntry, ListHead) \ for (Entry = (ListHead)->ForwardLink, NextEntry = Entry->ForwardLink; \ Entry != (ListHead); Entry = NextEntry, NextEntry = Entry->ForwardLink) /** Iterates over list entries in forward direction until element of number index is met First element has index 0 **/ #define LIST_FOR_UNTIL_INDEX(Entry, ListHead, Index, Iterator) \ for (Entry = (ListHead)->ForwardLink, Iterator = 0; Entry != (ListHead) && Iterator < Index; Entry = Entry->ForwardLink, Iterator++) /** Iterates over list entries in forward direction and counts the no of elements in list **/ #define LIST_COUNT(Entry, ListHead, Count) \ for (Entry = (ListHead)->ForwardLink, Count = 0;Entry != (ListHead); Entry = Entry->ForwardLink, Count++) /** The maximum buffer size for the Print function. This value depends on the UDK, it may change with a new version. **/ #define PCD_UEFI_LIB_MAX_PRINT_BUFFER_SIZE 320 /** Macros to get the number of days in a particular year **/ #define IS_LEAP_YEAR(Year) (!((Year) % 4) && (((Year) % 100) || !((Year) % 400))) #define DAYS_IN_YEAR(Year) (IS_LEAP_YEAR(Year) ? 366 : 365) // Helper macros to streamline the reading of code // NVDIMM_ERR will print out the line number and file, so no need to have // specific messages #define CHECK_RETURN_CODE(ReturnCode, Label) \ do { \ if (EFI_ERROR(ReturnCode)) { \ NVDIMM_ERR("Failure on function: %d", ReturnCode); \ goto Label; \ } \ } while (0) // Return if failure #define CHECK_RESULT(Call, Label) \ do { \ ReturnCode = Call; \ if (EFI_ERROR(ReturnCode)) { \ NVDIMM_ERR("Failure with %s. RC: %d", #Call, ReturnCode); \ goto Label; \ } \ } while (0) // Return if failure #define CHECK_RESULT_SET_NVM_STATUS(Call, pNvmStatus, NvmStatusCode, Label) \ do { \ ReturnCode = Call; \ if (EFI_ERROR(ReturnCode)) { \ NVDIMM_ERR("Failure with %s. RC: %d", #Call, ReturnCode); \ *pNvmStatus = NvmStatusCode; \ goto Label; \ } \ } while (0) // Return if success #define CHECK_RESULT_SUCCESS(Call, Label) \ do { \ ReturnCode = Call; \ if (ReturnCode == EFI_SUCCESS) { \ goto Label; \ } \ } while (0) // Ignore error code, but print it out #define CHECK_RESULT_CONTINUE(Call) \ do { \ EFI_STATUS ReturnCodeTemp = Call; \ if (EFI_ERROR(ReturnCodeTemp)) { \ NVDIMM_WARN("Ignoring failure with %s. RC: %d", #Call, ReturnCodeTemp); \ } \ } while (0) // Make a non-terminating function call, but if EFI_SUCCESS is returned // don't overwrite any previous error in ReturnCode. If a failure is returned // then it's ok to overwrite the previous ReturnCode. // This is useful for executing all possible function calls before exiting // with any error that is received from any function call. // Note: Requires definition of PreservedReturnCode and overwriting the ReturnCode // at the end of the function if required. #define CHECK_RESULT_CONTINUE_PRESERVE_ERROR(Call) \ do { \ EFI_STATUS ReturnCodeTemp = Call; \ if (EFI_ERROR(ReturnCodeTemp)) { \ NVDIMM_ERR("Ignoring but preserving failure with %s. RC: %d", #Call, ReturnCodeTemp); \ PreservedReturnCode = ReturnCodeTemp; \ } \ } while (0) #define CHECK_RESULT_MALLOC(Pointer, Call, Label) \ do { \ Pointer = Call; \ if (Pointer == NULL) { \ NVDIMM_ERR("Failed to allocate memory to %s", #Pointer); \ ReturnCode = EFI_OUT_OF_RESOURCES; \ goto Label; \ } \ } while (0) #define CHECK_RESULT_FILE(Call, Label) \ do { \ ReturnCode = Call; \ if (EFI_ERROR(ReturnCode)) { \ NVDIMM_ERR("Error in file operation %s", #Call); \ ResetCmdStatus(pCommandStatus, NVM_ERR_DUMP_FILE_OPERATION_FAILED); \ goto Label; \ } \ } while (0) // Go to Label if not true #define CHECK_NOT_TRUE(Call, Label) \ do { \ if (TRUE != (Call)) { \ NVDIMM_ERR("Statement %s is not true", #Call); \ goto Label; \ } \ } while (0) #define CHECK_NULL_ARG(Argument, Label) \ do { \ if (NULL == (VOID *)Argument) { \ NVDIMM_ERR("Argument %s is NULL. Exiting", #Argument); \ ReturnCode = EFI_INVALID_PARAMETER; \ goto Label; \ } \ } while (0) /** Get a variable from UEFI RunTime services. This will use the Runtime Services call GetVariable to get a variable. @param VarName The name of the variable in question @param VendorGuid A unique identifier for the vendor @param BufferSize Pointer to the UINTN size of Buffer @param Buffer Pointer buffer to get variable value into @retval EFI_SUCCESS The variable's value was retrieved successfully @retval other An error occurred **/ #ifndef OS_BUILD #define GET_VARIABLE(VarName,VendorGuid,BufferSize,Buffer) \ (gRT->GetVariable((CHAR16*)VarName, \ &VendorGuid, \ 0, \ BufferSize, \ Buffer)) #else #define GET_VARIABLE(VarName,VendorGuid,BufferSize,Buffer) preferences_get_var(VarName,VendorGuid,Buffer,BufferSize) #define GET_VARIABLE_STR(VarName,VendorGuid,BufferSize,Buffer) preferences_get_var_string_wide(VarName,VendorGuid,Buffer,BufferSize) #endif /** Set a Non-Volatile UEFI RunTime variable. This will use the Runtime Services call SetVariable to set a non-volatile variable. @param VarName The name of the variable in question @param VendorGuid A unique identifier for the vendor @param BufferSize UINTN size of Buffer @param Buffer Pointer to value to set variable to @retval EFI_SUCCESS The variable was changed successfully @retval other An error occurred **/ #ifndef OS_BUILD #define SET_VARIABLE_NV(VarName,VendorGuid,BufferSize,Buffer) \ (gRT->SetVariable((CHAR16*)VarName, \ &VendorGuid, \ EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS, \ BufferSize, \ (VOID*)Buffer)) #else #define SET_VARIABLE_NV(VarName,VendorGuid,BufferSize,Buffer) preferences_set_var(VarName,VendorGuid,(void*)Buffer,BufferSize); preferences_flush_the_file() #define SET_STR_VARIABLE_NV(VarName,VendorGuid,VarVal) preferences_set_var_string_wide(VarName,VendorGuid, VarVal); preferences_flush_the_file() #endif /** Set a Volatile UEFI RunTime variable. This will use the Runtime Services call SetVariable to set a non-volatile variable. @param VarName The name of the variable in question @param VendorGuid A unique identifier for the vendor @param BufferSize UINTN size of Buffer @param Buffer Pointer to value to set variable to @retval EFI_SUCCESS The variable was changed successfully @retval other An error occurred **/ #ifndef OS_BUILD #define SET_VARIABLE(VarName,VendorGuid,BufferSize,Buffer) \ (gRT->SetVariable((CHAR16*)VarName, \ &VendorGuid, \ EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS, \ BufferSize, \ (VOID*)Buffer)) #else #define SET_VARIABLE(VarName,VendorGuid,BufferSize,Buffer) preferences_set_var(VarName,VendorGuid,(void*)Buffer,BufferSize) #define SET_STR_VARIABLE(VarName,VendorGuid,VarVal) preferences_set_var_string_wide(VarName,VendorGuid, VarVal) #endif /** Removes all whitespace from before, after, and inside a passed string @param[IN, OUT] buffer - The string to remove whitespace from **/ VOID RemoveAllWhiteSpace( CHAR16* buffer); /** Returns the value of the environment variable with the given name. @param[in] pVarName Unicode name of the variable to retrieve @retval NULL if the shell protocol could not be located or if the variable is not defined in the system @retval pointer to the Unicode string containing the variable value **/ CHAR16 * GetEnvVariable( IN CHAR16 *pVarName ); /** Checks if the Config Protocol version is right. @param[in] *pConfigProtocol, instance of the protocol to check @retval EFI_SUCCESS if the version matches. @retval EFI_INVALID_PARAMETER if the passed parameter equals to NULL. @retval EFI_INCOMPATIBLE_VERSION when the version is wrong. **/ EFI_STATUS CheckConfigProtocolVersion( IN EFI_DCPMM_CONFIG2_PROTOCOL *pConfigProtocol ); /** Generates namespace type string, caller must free it @param[in] Type, value corresponding to namespace type. @retval Pointer to type string **/ CHAR16* NamespaceTypeToString( IN UINT8 Type ); /** Generates string from diagnostic output to print and frees the diagnostic structure @param[in] Type, pointer to type structure @retval Pointer to type string **/ CHAR16 * DiagnosticResultToStr( IN DIAG_INFO *pResult ); /** Generates pointer to string with value corresponding to health state Caller is responsible for FreePool on this pointer **/ CHAR16* NamespaceHealthToString( IN UINT16 Health ); /** Check if LIST_ENTRY list is initialized @param[in] ListHead list head @retval BOOLEAN list initialization status **/ BOOLEAN IsListInitialized( IN LIST_ENTRY ListHead ); /** Calculate checksum using Fletcher64 algorithm and compares it at the given offset. The length parameter must be aligned to 4 (32bit). @param[in] pAddress Starting address of area to calculate checksum on @param[in] Length Length of area over which checksum is calculated @param[in, out] pChecksum, the pointer where the checksum lives in @param[in] Insert, flag telling if the checksum should be inserted at the specified address or just compared to it @retval TRUE if the compared checksums are equal @retval FALSE if the checksums differ or the input parameters are invalid (a NULL was passed or the length is not aligned) **/ BOOLEAN ChecksumOperations( IN VOID *pAddress, IN UINT64 Length, IN OUT UINT64 *pChecksum, IN BOOLEAN Insert ); /** Compares the two provided 128bit unsigned ints. @param[in] LeftValue is the first 128bit uint. @param[in] RightValue is the second 128bit uint. @retval -1 when the LeftValue is smaller than the RightValue @retval 0 when the provided values are the same @retval 1 when the LeftValue is bigger than the RightValue **/ INT8 CompareUint128( IN UINT128 LeftValue, IN UINT128 RightValue ); /** The Print function is not able to print long strings. This function is dividing the input string to safe lengths and prints all of the parts. @param[in] pString - the Unicode string to be printed **/ VOID LongPrint( IN CHAR16 *pString ); /** Tokenize a string by the specified delimiter and update the input to the remainder. NOTE: Returned token needs to be freed by the caller **/ CHAR16 *StrTok(CHAR16 **input, CONST CHAR16 delim); /** Tokenize provided ASCII string @param[in] Input Input string @param[in] Delimiter Delimiter character @retval Pointer to token string **/ CHAR8* AsciiStrTok( IN CHAR8** Input, IN CONST CHAR8 Delimiter ); /** Split a string by the specified delimiter and return the split string as a string array. The caller is responsible for a memory deallocation of the returned array and its elements. @param[in] pInput the input string to split @param[in] Delimiter delimiter to split the string @param[out] pArraySize array size will be put here @retval NULL at least one of parameters is NULL or memory allocation failure @retval the split input string as an array **/ CHAR16 ** StrSplit( IN CHAR16 *pInput, IN CHAR16 Delimiter, OUT UINT32 *pArraySize ); /** Split an ASCII string by the specified delimiter and return the split string as a string array. The caller is responsible for a memory deallocation of the returned array and its elements. @param[in] pInput the input string to split @param[in] Delimiter delimiter to split the string @param[out] pArraySize array size will be put here @retval NULL at least one of parameters is NULL or memory allocation failure @retval the split input string as an array **/ CHAR8 ** AsciiStrSplit( IN CHAR8 *pInput, IN CHAR8 Delimiter, OUT UINT32 *pArraySize ); /** First free elements of array and then free the array This does NOT set pointer to array to NULL @param[in,out] ppStringArray array of strings @param[in] ArraySize number of strings **/ VOID FreeStringArray( IN OUT CHAR16 **ppStringArray, IN UINT32 ArraySize ); /** Copy of FreeStringArray, used for avoiding static code analysis complaint @param[in,out] ppStringArray array of strings @param[in] ArraySize number of strings **/ VOID FreeStringArrayAscii( IN OUT CHAR8 **ppStringArray, IN UINT32 ArraySize ); /** Open the specified protocol. If the user does not provide a handle, the function will try to match the driver or the controller handle based on the provided protocol GUID. No need to call close protocol because of the way it is opened. @param[in] Guid is the EFI GUID of the protocol we want to open. @param[out] ppProtocol is the pointer to a pointer where the opened protocol instance address will be returned. @param[in] pHandle a handle that we want to open the protocol on. OPTIONAL @retval EFI_SUCCESS if everything went successfully. @retval EFI_INVALID_ARGUMENT if ppProtocol is NULL. Other return values from functions: getControllerHandle getDriverHandle gBS->OpenProtocol **/ EFI_STATUS OpenNvmDimmProtocol( IN EFI_GUID Guid, OUT VOID **ppProtocol, IN EFI_HANDLE pHandle OPTIONAL ); /** Return a first found handle for specified protocol. @param[in] pProtocolGuid protocol that EFI handle will be found for. @param[out] pDriverHandle is the pointer to the result handle. @retval EFI_INVALID_PARAMETER if one or more input parameters are NULL. @retval all of the LocateHandleBuffer return values. **/ EFI_STATUS GetDriverHandle( IN EFI_GUID *pProtocolGuid, OUT EFI_HANDLE *pDriverHandle ); /** Open file or create new file in text mode. @param[in] pArgFilePath path to a file that will be opened @param[out] pFileHandle output handler @param[in, optional] pCurrentDirectory is the current directory path to where we should start to search for the file. @param[in] CreateFileFlag TRUE to create new file or FALSE to open existing file @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER pFilePath is NULL or empty or pFileHandle is NULL @retval EFI_PROTOCOL_ERROR if there is no EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **/ EFI_STATUS OpenFileText( IN CHAR16 *pArgFilePath, OUT EFI_FILE_HANDLE *pFileHandle, IN CONST CHAR16 *pCurrentDirectory OPTIONAL, IN BOOLEAN CreateFileFlag ); /** Open file or create new file in binary mode. @param[in] pArgFilePath path to a file that will be opened @param[out] pFileHandle output handler @param[in, optional] pCurrentDirectory is the current directory path to where we should start to search for the file. @param[in] CreateFileFlag TRUE to create new file or FALSE to open existing file @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER pFilePath is NULL or empty or pFileHandle is NULL @retval EFI_PROTOCOL_ERROR if there is no EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **/ EFI_STATUS OpenFileBinary( IN CHAR16 *pArgFilePath, OUT EFI_FILE_HANDLE *pFileHandle, IN CONST CHAR16 *pCurrentDirectory OPTIONAL, IN BOOLEAN CreateFileFlag ); /** Open file or create new file with the proper flags. @param[in] pArgFilePath path to a file that will be opened @param[out] pFileHandle output handler @param[in, optional] pCurrentDirectory is the current directory path to where we should start to search for the file. @param[in] CreateFileFlag - TRUE to create new file or FALSE to open existing file @param[in] binary - use binary open @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER pFilePath is NULL or empty or pFileHandle is NULL @retval EFI_PROTOCOL_ERROR if there is no EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **/ EFI_STATUS OpenFileWithFlag( IN CHAR16 *pArgFilePath, OUT EFI_FILE_HANDLE *pFileHandle, IN CONST CHAR16 *pCurrentDirectory OPTIONAL, IN BOOLEAN CreateFileFlag, BOOLEAN binary ); /** Open file or create new file based on device path protocol. @param[in] pArgFilePath Pointer to path to a file that will be opened @param[in] pDevicePath Pointer to instance of device path protocol @param[in] CreateFileFlag TRUE to create new file or FALSE to open existing file @param[out] pFileHandle Output file handler @retval EFI_SUCCESS File opened or created @retval EFI_INVALID_PARAMETER Input parameter is invalid @retval Others From LocateDevicePath, OpenProtocol, OpenVolume and Open **/ EFI_STATUS OpenFileByDevice( IN CHAR16 *pArgFilePath, IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath, IN BOOLEAN CreateFileFlag, OUT EFI_FILE_HANDLE *pFileHandle ); /** Converts the dimm health state reason to its HII string equivalent @param[in] @param[in] HiiHandle - handle for hii @param[in] HealthStateReason The health state reason to be converted into its HII string @param[out] ppHealthStateStr A pointer to the HII health state string. Dynamically allocated memory and must be released by calling function. @retval EFI_OUT_OF_RESOURCES if there is no space available to allocate memory for string @retval EFI_INVALID_PARAMETER if one or more input parameters are invalid @retval EFI_SUCCESS The conversion was successful **/ EFI_STATUS ConvertHealthStateReasonToHiiStr( IN EFI_HANDLE HiiHandle, IN UINT16 HealthStatusReason, OUT CHAR16 **ppHealthStatusReasonStr ); /** Get Dimm Info by device handle Scan the dimm list for a DimmInfo identified by device handle @param[in] DeviceHandle Device Handle of the dimm @param[in] pDimmInfo Array of DimmInfo @param[in] DimmCount Size of DimmInfo array @param[out] ppRequestedDimmInfo Pointer to the request DimmInfo struct @retval EFI_INVALID_PARAMETER pDimmInfo or pRequestedDimmInfo is NULL @retval EFI_SUCCESS Success **/ EFI_STATUS GetDimmInfoByHandle( IN UINT32 DeviceHandle, IN DIMM_INFO *pDimmInfo, IN UINT32 DimmCount, OUT DIMM_INFO **ppRequestedDimmInfo ); /** Converts the Dimm IDs within a region to its HII string equivalent @param[in] pRegionInfo The Region info with DimmID and DimmCount its HII string @param[in] pNvmDimmConfigProtocol A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] DimmIdentifier Dimm identifier preference @param[out] ppDimmIdStr A pointer to the HII DimmId string. Dynamically allocated memory and must be released by calling function. @retval EFI_OUT_OF_RESOURCES if there is no space available to allocate memory for string @retval EFI_INVALID_PARAMETER if one or more input parameters are invalid @retval EFI_SUCCESS The conversion was successful **/ EFI_STATUS ConvertRegionDimmIdsToDimmListStr( IN REGION_INFO *pRegionInfo, IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN UINT8 DimmIdentifier, OUT CHAR16 **ppDimmIdStr ); /** Open file handle of root directory from given path @param[in] pDevicePath - path to file @param[out] pFileHandle - root directory file handle **/ EFI_STATUS OpenRootFileVolume( IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath, OUT EFI_FILE_HANDLE *pRootDirHandle ); /** Returns the size of the specified file. @param[in] FileHandle - handle to the opened file that we want to get the size for. @param[out] pFileSize - the result file size on bytes. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER if one of the input parameters is a NULL. Other return values associated with the GetInfo callback. **/ EFI_STATUS GetFileSize( IN EFI_FILE_HANDLE FileHandle, OUT UINT64 *pFileSize ); /** Return the NvmDimmController handle. @param[out] pControllerHandle is the pointer to the result handle. @retval EFI_INVALID_PARAMETER if the pControllerHandle is NULL. @retval all of the LocateHandleBuffer return values. **/ EFI_STATUS GetControllerHandle( OUT EFI_HANDLE *pControllerHandle ); /** Convert all Interleave settings to string WARNING! *ppIoString can be reallocated. Calling function is responsible for its freeing. Additionally *ppIoString must be dynamically allocated. @param[in] PersistentSize - Persistent size of interleave set in DIMM @param[in] NumberOfInterleavedDimms - Number of interleaved DIMMs @param[in] ImcInterleaving - iMC interleaving bit map @param[in] ChannelInterleaving - Channel interleaving bit map @param[out] ppString - output string. **/ VOID InterleaveSettingsToString( IN UINT64 PersistentSize, IN UINT8 NumberOfInterleavedDimms, IN UINT8 ImcInterleaving, IN UINT8 ChannelInterleaving, OUT CHAR16 **ppString ); /** Convert Channel Interleaving value to output settings string @param[in] Interleaving - Channel Interleave BitMask @retval appropriate string @retval NULL - if Interleaving value is incorrect **/ CONST CHAR16 * ParseChannelInterleavingValue( IN UINT8 Interleaving ); /** Convert iMC Interleaving value to output settings string @param[in] Interleaving - iMC Interleave BitMask @retval appropriate string @retval NULL - if Interleaving value is incorrect **/ CONST CHAR16 * ParseImcInterleavingValue( IN UINT8 Interleaving ); /** Appends a formatted Unicode string to a Null-terminated Unicode string This function appends a formatted Unicode string to the Null-terminated Unicode string specified by String. String is optional and may be NULL. Storage for the formatted Unicode string returned is allocated using AllocatePool(). The pointer to the appended string is returned. The caller is responsible for freeing the returned string. This function also calls FreePool on the old pString buffer if it is not NULL. So the caller does not need to free the previous buffer. If String is not NULL and not aligned on a 16-bit boundary, then ASSERT(). If FormatString is NULL, then ASSERT(). If FormatString is not aligned on a 16-bit boundary, then ASSERT(). @param[in] String A Null-terminated Unicode string. @param[in] FormatString A Null-terminated Unicode format string. @param[in] ... The variable argument list whose contents are accessed based on the format string specified by FormatString. @retval NULL There was not enough available memory. @return Null-terminated Unicode string is that is the formatted string appended to String. **/ CHAR16* EFIAPI CatSPrintClean( IN CHAR16 *String, OPTIONAL IN CONST CHAR16 *FormatString, ... ); /** Appends a formatted Unicode string with arguments to a pre-allocated null-terminated Unicode string provided by the caller with length of DestStringMaxLength. @param[in] DestString A Null-terminated Unicode string of size DestStringMaxLength @param[in] DestStringMaxLength The maximum number of CHAR16 characters that will fit into DestString @param[in] FormatString A Null-terminated Unicode format string. @param[in] ... The variable argument list whose contents are accessed based on the format string specified by FormatString. **/ EFI_STATUS CatSPrintNCopy( IN OUT CHAR16 *pDestString, IN UINT16 DestStringMaxLength, IN CONST CHAR16 *pFormatString, ... ); /** Function that allows for nicely formatted HEX & ASCII debug output. It can be used to inspect memory objects without a need for debugger @param[in] pBuffer Pointer to an arbitrary object @param[in] Bytes Number of bytes to display **/ VOID HexDebugPrint( IN VOID *pBuffer, IN UINT32 Bytes ); /** Function that allows for nicely formatted HEX & ASCII console output. It can be used to inspect memory objects without a need for debugger or dumping raw DIMM data. @param[in] pBuffer Pointer to an arbitrary object @param[in] Bytes Number of bytes to display **/ VOID HexPrint( IN VOID *pBuffer, IN UINT32 Bytes ); /** Case Insensitive StrCmp @param[in] pFirstString - first string for comparison @param[in] pSecondString - second string for comparison @retval Negative number if strings don't match and pFirstString < pSecondString @retval 0 if strings match @retval Positive number if strings don't match and pFirstString > pSecondString **/ INTN StrICmp( IN CONST CHAR16 *pFirstString, IN CONST CHAR16 *pSecondString ); /** Checks if the user-inputted desired ARS status matches with the current system-wide ARS status. @param[in] DesiredARSStatus Desired value of the ARS status to match against @param[out] pARSStatusMatched Pointer to a boolean value which shows if the current system ARS status matches the desired one. @retval EFI_SUCCESS if there were no problems @retval EFI_INVALID_PARAMETER if one of the input parameters is a NULL, or an invalid value. **/ EFI_STATUS MatchCurrentARSStatus( IN UINT8 DesiredARSStatus, OUT BOOLEAN *pARSStatusMatched ); /** Function to write a line of unicode text to a file. If Handle is NULL, return error. If Buffer is NULL, return error. @param[in] Handle FileHandle to write to @param[in] Buffer Buffer to write @retval EFI_SUCCESS The data was written. @retval other Error codes from Write function. **/ EFI_STATUS EFIAPI WriteAsciiLine( IN EFI_FILE_HANDLE Handle, IN VOID *pBuffer ); /** Try to find a sought pointer in an array @param[in] pPointersArray Array of pointers @param[in] PointersNum Number of pointers in array @param[in] pSoughtPointer Sought pointer @retval TRUE if pSoughtPointer has been found in the array @retval FALSE otherwise **/ BOOLEAN IsPointerInArray( IN VOID *pPointersArray[], IN UINT32 PointersNum, IN VOID *pSoughtPointer ); /** Check if given language is supported (is on supported language list) @param[in] pSupportedLanguages - list of supported languages @param[in] pLanguage - language to verify if is supported @param[in] Rfc4646Language - language abbreviation is compatible with Rfc4646 standard @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_UNSUPPORTED - language is not supported @retval EFI_SUCCESS Is supported **/ EFI_STATUS CheckIfLanguageIsSupported( IN CONST CHAR8 *pSupportedLanguages, IN CONST CHAR8 *pLanguage, IN BOOLEAN Rfc4646Language ); /** Convert a character to upper case @param[in] InChar - character to up @retval - upper character **/ CHAR16 NvmToUpper( IN CHAR16 InChar ); /** Calculate a power of base. @param[in] Base base @param[in] Exponent exponent @retval Base ^ Exponent **/ UINT64 Pow( IN UINT64 Base, IN UINT32 Exponent ); /** Read file to given buffer * WARNING * caller is responsible for freeing ppFileBuffer @param[in] pFilePath - file path @param[in] pDevicePath - handle to obtain generic path/location information concerning the physical device or logical device. The device path describes the location of the device the handle is for. @param[in] MaxFileSize - if file is bigger skip read and return error @param[in] AsBinary - To open file as a binary file or not. (Windows will replace \r\n with \n if not binary file) @param[out] pFileSize - number of bytes written to buffer @param[out] ppFileBuffer - output buffer * WARNING * caller is responsible for freeing @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_NOT_STARTED Test was not executed @retval EFI_OUT_OF_RESOURCES if memory allocation fails. @retval EFI_SUCCESS All Ok **/ EFI_STATUS FileRead( IN CHAR16 *pFilePath, IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath, IN CONST UINT64 MaxFileSize, IN BOOLEAN AsBinary, OUT UINT64 *pFileSize, OUT VOID **ppFileBuffer ); /** Read ASCII line from a file. The function ignores carriage return chars. @param FileHandle handle to a file @param pLine output buffer that will be filled with read line @param LineSize size of pLine buffer @param pEndOfFile output variable to report about end of file @retval EFI_SUCCESS @retval EFI_BUFFER_TOO_SMALL when pLine buffer is too small @retval EFI_INVALID_PARAMETER pLine or pEndOfFile is NULL **/ EFI_STATUS ReadAsciiLineFromFile( IN EFI_FILE_HANDLE FileHandle, OUT CHAR8 *pLine, IN INT32 LineSize, OUT BOOLEAN *pEndOfFile ); /** Clear memory containing string @param[in] pString - pointer to string to be cleared **/ VOID CleanStringMemory( IN CHAR8 *pString ); /** Clear memory containing unicode string @param[in] pString - pointer to string to be cleared **/ VOID CleanUnicodeStringMemory( IN CHAR16 *pString ); /** Get linked list size @param[in] pListHead List head @param[out] pListSize Counted number of items in the list @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER At least one of the input parameters equals NULL **/ EFI_STATUS GetListSize( IN LIST_ENTRY *pListHead, OUT UINT32 *pListSize ); /** Implementation of public algorithm to calculate least common multiple of two numbers @param[in] A First number @param[in] B Second number @retval Least common multiple **/ UINT64 FindLeastCommonMultiple( IN UINT64 A, IN UINT64 B ); /** Trim white spaces from the begin and end of string @param[in, out] pString Null terminated string that will be trimmed @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameters is NULL @retval EFI_BAD_BUFFER_SIZE Size of input string is bigger than MAX_INT32 **/ EFI_STATUS TrimString( IN OUT CHAR16 *pString ); /** Removes all white spaces from string @param[in] pInputBuffer Pointer to string to remove white spaces @param[out] pOutputBuffer Pointer to string with no white spaces @param[in, out] OutputBufferLength On input, length of buffer (in CHAR16), on output, length of string with no white spaces, without null-terminator @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL or string length is 0 @retval EFI_BUFFER_TOO_SMALL Output buffer is too small **/ EFI_STATUS RemoveWhiteSpaces( IN CHAR8 *pInputBuffer, OUT CHAR8 *pOutputBuffer, IN OUT UINT64 *pOutputBufferLength ); /** Convert Last Shutdown Status to string @param[in] LastShutdownStatus structure @param[in] FwVer Struct representing firmware version @retval CLI string representation of last shutdown status **/ CHAR16* LastShutdownStatusToStr( IN LAST_SHUTDOWN_STATUS_DETAILS_COMBINED LastShutdownStatus, IN FIRMWARE_VERSION FwVer ); /** Convert modes supported to string @param[in] ModesSupported, bits define modes supported @retval CLI string representation of modes supported **/ CHAR16* ModesSupportedToStr( IN UINT8 ModesSupported ); /** Convert software triggers enabled to string @param[in] SoftwareTriggersEnabled, bits define triggers that are enabled @retval CLI string representation of enabled triggers **/ CHAR16* SoftwareTriggersEnabledToStr( IN UINT64 SoftwareTriggersEnabled ); /** Convert Security Capabilities to string @param[in] SecurityCapabilities, bits define capabilities @retval CLI string representation of security capabilities **/ CHAR16* SecurityCapabilitiesToStr( IN UINT8 SecurityCapabilities ); /** Convert Dimm security state to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] Dimm security state @retval String representation of Dimm's security state **/ CHAR16* SecurityToString( IN EFI_HANDLE HiiHandle, IN UINT8 SecurityState ); /** Retrieve the number of bits set in a number Based on Brian Kernighan's Algorithm @param[in] Number Number in which number of bits set is to be counted @param[out] pNumOfBitsSet Number of bits set **/ EFI_STATUS CountNumOfBitsSet( IN UINT64 Number, OUT UINT8 *pNumOfBitsSet ); /** Retrieve the bitmap for NumOfChannelWays @param[in] NumOfChannelWays Number of ChannelWays or Number of Dimms used in an Interleave Set @param[out] pBitField Bitmap based on PCAT 2.0 Type 1 Table for ChannelWays **/ EFI_STATUS GetBitFieldForNumOfChannelWays( IN UINT64 NumOfChannelWays, OUT UINT16 *pBitField ); /** Convert dimm's security state bitmask to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] SecurityStateBitmask, bits define dimm's security state @retval String representation of Dimm's security state **/ CHAR16* SecurityStateBitmaskToString( IN EFI_HANDLE HiiHandle, IN UINT32 SecurityStateBitmask ); /** Convert dimm's SVN Downgrade Opt-In to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] SecurityOptIn, bits define dimm's security opt-in value @retval String representation of Dimm's SVN Downgrade opt-in **/ CHAR16* SVNDowngradeOptInToString( IN EFI_HANDLE HiiHandle, IN UINT32 OptInValue ); /** Convert dimm's Secure Erase Policy Opt-In to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] SecurityOptIn, bits define dimm's security opt-in value @retval String representation of Dimm's Secure erase policy opt-in **/ CHAR16* SecureErasePolicyOptInToString( IN EFI_HANDLE HiiHandle, IN UINT32 OptInValue ); /** Convert dimm's S3 Resume Opt-In to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] SecurityOptIn, bits define dimm's security opt-in value @retval String representation of Dimm's S3 Resume opt-in **/ CHAR16* S3ResumeOptInToString( IN EFI_HANDLE HiiHandle, IN UINT32 OptInValue ); /** Convert dimm's Fw Activate Opt-In to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] SecurityOptIn, bits define dimm's security opt-in value @retval String representation of Dimm's Fw Activate opt-in **/ CHAR16* FwActivateOptInToString( IN EFI_HANDLE HiiHandle, IN UINT32 OptInValue ); /** Convert long op status value to its respective string @param[in] HiiHandle Pointer to HII handle @param[in] LongOpStatus status value @retval CLI string representation of long op status **/ CHAR16* LongOpStatusToStr( IN EFI_HANDLE HiiHandle, IN UINT8 LongOpStatus ); /** Convert dimm's boot status bitmask to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] BootStatusBitmask, bits define the boot status @retval CLI/HII string representation of dimm's boot status **/ CHAR16* BootStatusBitmaskToStr( IN EFI_HANDLE HiiHandle, IN UINT16 BootStatusBitmask ); /** Convert string value to double @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] pString String value to convert @param[out] pOutValue Target double value @retval EFI_INVALID_PARAMETER No valid value inside @retval EFI_SUCCESS Conversion successful **/ EFI_STATUS StringToDouble( IN EFI_HANDLE HiiHandle, IN CHAR16 *pString, OUT double *pOutValue ); /** Compare a PackageSparing capability, encryption, soft SKU capabilities and SKU mode types. @param[in] SkuInformation1 - first SkuInformation to compare @param[in] SkuInformation2 - second SkuInformation to compare @retval NVM_SUCCESS - if everything went fine @retval NVM_ERR_DIMM_SKU_MODE_MISMATCH - if mode conflict occurred @retval NVM_ERR_DIMM_SKU_SECURITY_MISMATCH - if security mode conflict occurred **/ NvmStatusCode SkuComparison( IN UINT32 SkuInformation1, IN UINT32 SkuInformation2 ); /** Check if SKU conflict occurred. Any mixed modes between DIMMs are prohibited on a platform. @param[in] pDimmInfo1 - first DIMM_INFO to compare SKU mode @param[in] pDimmInfo2 - second DIMM_INFO to compare SKU mode @param[out] pSkuModeMismatch - pointer to a BOOLEAN value that will represent result of comparison @retval - Appropriate CLI return code **/ EFI_STATUS IsSkuModeMismatch( IN DIMM_INFO *pDimmInfo1 OPTIONAL, IN DIMM_INFO *pDimmInfo2 OPTIONAL, OUT BOOLEAN *pSkuModeMismatch ); /** Convert type to string @param[in] MemoryType, integer define type @retval CLI string representation of memory type **/ CHAR16* MemoryTypeToStr( IN UINT8 MemoryType ); /** Sort Linked List by using Bubble Sort. @param[in, out] LIST HEAD to sort @param[in] Compare Pointer to function that is needed for items comparing. It should return: -1 if "first < second" 0 if "first == second" 1 if "first > second" @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS BubbleSortLinkedList( IN OUT LIST_ENTRY *pList, IN INT32 (*Compare) (VOID *first, VOID *second) ); /** Sort an array by using Bubble Sort. @param[in, out] pArray Array to sort @param[in] Count Number of items in array @param[in] ItemSize Size of item in bytes @param[in] Compare Pointer to function that is needed for items comparing. It should return: -1 if "first < second" 0 if "first == second" 1 if "first > second" @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS BubbleSort( IN OUT VOID *pArray, IN UINT32 Count, IN UINT32 ItemSize, IN INT32 (*Compare) (VOID *first, VOID *second) ); /** Populates the units string based on the particular capacity unit @param[in] pData A pointer to the main HII data structure @param[in] Units The input unit to be converted into its HII string @param[out] ppUnitsStr A pointer to the HII units string. Dynamically allocated memory and must be released by calling function. @retval EFI_OUT_OF_RESOURCES if there is no space available to allocate memory for units string @retval EFI_INVALID_PARAMETER if one or more input parameters are invalid @retval EFI_SUCCESS The conversion was successful **/ EFI_STATUS UnitsToStr ( IN EFI_HII_HANDLE HiiHandle, IN UINT16 Units, OUT CHAR16 **ppUnitsStr ); /** Convert last firmware update status to string. The caller function is obligated to free memory of the returned string. @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] Last Firmware update status value to convert @retval output string or NULL if memory allocation failed **/ CHAR16 * LastFwUpdateStatusToString( IN EFI_HANDLE HiiHandle, IN UINT8 LastFwUpdateStatus ); /** Convert quiesce required value to string. The caller function is obligated to free memory of the returned string. @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] Quiesce required value to convert @retval output string or NULL if memory allocation failed **/ CHAR16 * QuiesceRequiredToString( IN EFI_HANDLE HiiHandle, IN UINT8 QuiesceRequired ); /** Convert StagedFwActivatable to string. The caller function is obligated to free memory of the returned string. @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] Staged Fw activatable value to convert @retval output string or NULL if memory allocation failed **/ CHAR16 * StagedFwActivatableToString( IN EFI_HANDLE HiiHandle, IN UINT8 StagedFwActivatable ); /** Determines if an array, whose size is known in bytes has all elements as zero @param[in] pArray Pointer to the input array @param[in] ArraySize Array size in bytes @param[out] pAllElementsZero Pointer to a boolean that stores the result whether all array elements are zero @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ EFI_STATUS AllElementsInArrayZero( IN OUT VOID *pArray, IN UINT32 ArraySize, OUT BOOLEAN *pAllElementsZero ); /** Endian swap a uint32 value @param[in] OrigVal Value to modify @retval Value with the endian swap **/ UINT32 EndianSwapUint32( IN UINT32 OrigVal ); /** Endian swap a uint16 value @param[in] OrigVal Value to modify @retval Value with the endian swap **/ UINT16 EndianSwapUint16( IN UINT16 OrigVal ); /** Converts EPOCH time in number of seconds into a human readable time string @param[in] TimeInSeconds Number of seconds (EPOCH time) @retval Human readable time string **/ CHAR16 *GetTimeFormatString ( IN UINT64 TimeInSeconds, IN BOOLEAN verbose ); /** Convert goal status bitmask to its respective string @param[in] HiiHandle handle to the HII database that contains i18n strings @param[in] Status bits that define the goal status @retval CLI/HII string representation of goal status **/ CHAR16* GoalStatusToString( IN EFI_HANDLE HiiHandle, IN UINT8 Status ); /** Poll long operation status Polls the status of the background operation on the dimm. @param [in] pNvmDimmConfigProtocol Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param [in] DimmId Dimm ID of the dimm to poll status @param [in] OpcodeToPoll Specify an opcode to poll, 0 to poll regardless of opcode @param [in] SubOpcodeToPoll Specify an opcode to poll @param [in] Timeout for the background operation **/ EFI_STATUS PollLongOpStatus( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN UINT16 DimmId, IN UINT8 OpcodeToPoll OPTIONAL, IN UINT8 SubOpcodeToPoll OPTIONAL, IN UINT64 Timeout ); EFI_STATUS GetNSLabelMajorMinorVersion( IN UINT32 NamespaceLabelVersion, OUT UINT16 *pMajor, OUT UINT16 *pMinor ); /** Get basic information about the host server @param[out] pHostServerInfo pointer to a HOST_SERVER_INFO struct @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS GetHostServerInfo( OUT HOST_SERVER_INFO *pHostServerInfo ); /** Copies a source buffer to a destination buffer, and returns the destination buffer. @param DestinationBuffer The pointer to the destination buffer of the memory copy. @param DestLength The length in bytes of DestinationBuffer. @param SourceBuffer The pointer to the source buffer of the memory copy. @param Length The number of bytes to copy from SourceBuffer to DestinationBuffer. @return DestinationBuffer. **/ VOID * CopyMem_S( OUT VOID *DestinationBuffer, IN UINTN DestLength, IN CONST VOID *SourceBuffer, IN UINTN Length ); /** Retrieves Intel Dimm Config EFI vars User is responsible for freeing ppIntelDIMMConfig @param[out] pIntelDIMMConfig Pointer to struct to fill with EFI vars @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS RetrieveIntelDIMMConfig( OUT INTEL_DIMM_CONFIG **ppIntelDIMMConfig ); /** Get manageability state for Dimm @param[in] SubsystemVendorId the SubsystemVendorId @param[in] interfaceCodeNum the number of interface codes @param[in] interfaceCodes the interface codes @param[in] SubsystemDeviceId the subsystem device ID @param[in] fwMajor the fw major version @param[in] fwMinor the fw minor version @retval BOOLEAN whether or not dimm is manageable **/ BOOLEAN IsDimmManageableByValues( IN UINT16 SubsystemVendorId, IN UINT32 interfaceCodeNum, IN UINT16* interfaceCodes, IN UINT16 SubsystemDeviceId, IN UINT8 fwMajor, IN UINT8 fwMinor ); /** Check if the dimm interface code of this DIMM is supported @param[in] interfaceCodeNum the number of interface codes @param[in] interfaceCodes the interface codes @retval true if supported, false otherwise **/ BOOLEAN IsDimmInterfaceCodeSupportedByValues( IN UINT32 interfaceCodeNum, IN UINT16* interfaceCodes ); /** Check if the subsystem device ID of this DIMM is supported @param[in] SubsystemDeviceId the subsystem device ID @retval true if supported, false otherwise **/ BOOLEAN IsSubsystemDeviceIdSupportedByValues( IN UINT16 SubsystemDeviceId ); /** Check if current firmware API version is supported @param[in] major the major version @param[in] minor the minor version @retval true if supported, false otherwise **/ BOOLEAN IsFwApiVersionSupportedByValues( IN UINT8 major, IN UINT8 minor ); /** Convert controller revision id to string @param[in] Controller revision id @param[in] Subsystem Device Id for determining HW Gen @retval CLI string representation of the controller revision id **/ CHAR16* ControllerRidToStr( IN UINT16 ControllerRid, IN UINT16 SubsystemDeviceId ); /** Convert FIPS mode status to string @param[in] HiiHandle HII handle to access string dictionary @param[in] FIPSMode Response from GetFIPSMode firmware command @param[in] FwVer Firmware revision @param[in] ReturnCodeGetFIPSMode ReturnCode from GetFIPSMode API call, used to provide clearer error message @retval String representation of the FIPS mode status **/ CHAR16 * ConvertFIPSModeToString( IN EFI_HANDLE HiiHandle, IN FIPS_MODE FIPSMode, IN FIRMWARE_VERSION FwVer, IN EFI_STATUS ReturnCodeGetFIPSMode ); /** Set object status for DIMM_INFO @param[out] pCommandStatus Pointer to command status structure @param[in] pDimm DIMM_INFO for which the object status is being set @param[in] Status Object status to set **/ VOID SetObjStatusForDimmInfo( OUT COMMAND_STATUS *pCommandStatus, IN DIMM_INFO *pDimm, IN NVM_STATUS Status ); /** Set object status for DIMM_INFO @param[out] pCommandStatus Pointer to command status structure @param[in] pDimm DIMM_INFO for which the object status is being set @param[in] Status Object status to set @param[in] If TRUE - clear all other status before setting this one **/ VOID SetObjStatusForDimmInfoWithErase( OUT COMMAND_STATUS *pCommandStatus, IN DIMM_INFO *pDimm, IN NVM_STATUS Status, IN BOOLEAN EraseFirst ); /** Serialize a Tag ID @param[in] id the session tag id to be save/serialized **/ EFI_STATUS PbrDcpmmSerializeTagId( UINT32 id ); /** Deserialize a Tag ID @param[in] id - will contain the previously serialized tag id if exists, otherwise will contain the value of 'defaultId' @param[in] defaultId - the value to assign input param 'id' in the case the TagId wasn't previously serialized. **/ EFI_STATUS PbrDcpmmDeserializeTagId( UINT32 *id, UINT32 defaultId ); /** Converts a DIMM_INFO_ATTRIB_X attribute to a string @param[in] pAttrib - a DIMM_INFO_ATTRIB_X attribute to convert @param[in] pFormatStr - optional format string to use for conversion **/ CHAR16 * ConvertDimmInfoAttribToString( VOID *pAttrib, CHAR16* pFormatStr OPTIONAL ); /** Create a duplicate of a string without parsing any format strings. Caller is responsible for freeing the returned string. Max string length is MAX_STRING_LENGTH @param[in] StringToDuplicate - String to duplicate @param[out] pDuplicateString - Allocated copy of StringToDuplicate **/ EFI_STATUS DuplicateString( IN CHAR16 *StringToDuplicate, OUT CHAR16 **pDuplicateString ); /** Wrap the string (add \n) at the specified WrapPos by replacing a space character (' ') with a newline character. Used for the HII popup window. Make a copy of the MessageString so we can modify it if needed. @param[in] WrapPos - Line length limit (inclusive). Does not include "\n" or "\0" @param[in] MessageString - Original message string, is not modified @param[out] pWrappedString - Allocated copy of MessageString that is wrapped with "\n" **/ EFI_STATUS WrapString( IN UINT8 WrapPos, IN CHAR16 *MessageString, OUT CHAR16 **pWrappedString ); /** Guess an appropriate NVM_STATUS code from EFI_STATUS. For use when pCommandStatus is not an argument to a lower level function. Used currently to get specific errors relevant to the user out to the CLI but not many (especially lower-level) functions have pCommandStatus. Also the CLI printer doesn't use ReturnCode, only pCommandStatus. @param[in] ReturnCode - EFI_STATUS returned from function call @retval - Appropriate guess at the NVM_STATUS code **/ NVM_STATUS GuessNvmStatusFromReturnCode( IN EFI_STATUS ReturnCode ); #ifndef OS_BUILD /** Find serial attributes from SerialProtocol and set on serial driver @retval - Status of operation **/ EFI_STATUS SetSerialAttributes( VOID ); #endif #endif /** _UTILITY_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/UtilityIo.c000066400000000000000000001151101440615110200205260ustar00rootroot00000000000000/* This file contains functions that were in Utility.c that aren't self-contained or useful for all of our unit tested code. It allows us to limit the scope of the implementation that is needed to get unit tests running and happens to contain some API calls that we'll probably want to stub in the future as well. */ #include #include #include #include #include #include #include #include #include #include #ifdef OS_BUILD #include #include #else #include extern EFI_RUNTIME_SERVICES *gRT; #endif extern EFI_GUID gNvmDimmConfigProtocolGuid; /** Return a first found handle for specified protocol. @param[in] pProtocolGuid protocol that EFI handle will be found for. @param[out] pDriverHandle is the pointer to the result handle. @retval EFI_INVALID_PARAMETER if one or more input parameters are NULL. @retval all of the LocateHandleBuffer return values. **/ EFI_STATUS GetDriverHandle( IN EFI_GUID *pProtocolGuid, OUT EFI_HANDLE *pDriverHandle ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINTN HandleCount = 0; EFI_HANDLE *pHandleBuffer = NULL; NVDIMM_ENTRY(); if (pProtocolGuid == NULL || pDriverHandle == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pDriverHandle = NULL; /** Find the driver handle by searching for our custom NvmDimmConfig protocol **/ ReturnCode = gBS->LocateHandleBuffer(ByProtocol, pProtocolGuid, NULL, &HandleCount, &pHandleBuffer); if (EFI_ERROR(ReturnCode) || HandleCount != 1) { ReturnCode = EFI_NOT_FOUND; } else { *pDriverHandle = pHandleBuffer[0]; } Finish: FREE_POOL_SAFE(pHandleBuffer); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Return the NvmDimmController handle. @param[out] pControllerHandle is the pointer to the result handle. @retval EFI_INVALID_PARAMETER if the pControllerHandle is NULL. @retval all of the LocateHandleBuffer return values. **/ EFI_STATUS GetControllerHandle( OUT EFI_HANDLE *pControllerHandle ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; EFI_HANDLE DriverHandle = NULL; UINT32 Index = 0; EFI_HANDLE *pHandleBuffer = NULL; UINT64 HandleCount = 0; NVDIMM_ENTRY(); if (pControllerHandle == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pControllerHandle = NULL; ReturnCode = GetDriverHandle(&gNvmDimmConfigProtocolGuid, &DriverHandle); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Retrieve array of all handles in the handle database **/ ReturnCode = gBS->LocateHandleBuffer(ByProtocol, &gEfiDevicePathProtocolGuid, NULL, &HandleCount, &pHandleBuffer); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Could not find the handles set."); ReturnCode = EFI_NOT_FOUND; goto Finish; } /** Search all of the existing device path protocol instances for a device controlled by our driver. **/ for (Index = 0; Index < HandleCount; Index++) { ReturnCode = EfiTestManagedDevice(pHandleBuffer[Index], DriverHandle, // Our driver handle equals the driver binding handle so this call is valid &gEfiDevicePathProtocolGuid); // If the handle is managed - this is our controller. if (!EFI_ERROR(ReturnCode)) { *pControllerHandle = pHandleBuffer[Index]; break; } } Finish: if (pHandleBuffer != NULL) { FreePool(pHandleBuffer); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Open the specified protocol. If the user does not provide a handle, the function will try to match the driver or the controller handle basing on the provided protocol GUID. No need to call close protocol because of the way it is opened. @param[in] Guid is the EFI GUID of the protocol we want to open. @param[out] ppProtocol is the pointer to a pointer where the opened protocol instance address will be returned. @param[in] pHandle a handle that we want to open the protocol on. OPTIONAL @retval EFI_SUCCESS if everything went successful. @retval EFI_INVALID_ARGUMENT if ppProtocol is NULL. Other return values from functions: getControllerHandle getDriverHandle gBS->OpenProtocol **/ EFI_STATUS OpenNvmDimmProtocol( IN EFI_GUID Guid, OUT VOID **ppProtocol, IN EFI_HANDLE pHandle OPTIONAL ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_HANDLE DeviceHandle = NULL; NVDIMM_ENTRY(); if (pHandle == NULL) { if (CompareGuid(&Guid, &gEfiDevicePathProtocolGuid)) { ReturnCode = GetControllerHandle(&DeviceHandle); } else { ReturnCode = GetDriverHandle(&gNvmDimmConfigProtocolGuid, &DeviceHandle); } if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Could not determine the target device type, error = " FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } } else { DeviceHandle = pHandle; } ReturnCode = gBS->OpenProtocol( DeviceHandle, &Guid, ppProtocol, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_ALREADY_STARTED) { ReturnCode = EFI_SUCCESS; } else { NVDIMM_WARN("Failed to open NvmDimmProtocol, error = " FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } } if (CompareGuid(&Guid, &gNvmDimmConfigProtocolGuid)) { ReturnCode = CheckConfigProtocolVersion((EFI_DCPMM_CONFIG2_PROTOCOL *) *ppProtocol); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to get the proper config protocol."); ppProtocol = NULL; } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Open file or create new file in text mode. @param[in] pArgFilePath path to a file that will be opened @param[out] pFileHandle output handler @param[in, optional] pCurrentDirectory is the current directory path to where we should start to search for the file. @param[in] CreateFileFlag - TRUE to create new file or FALSE to open existing file @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER pFilePath is NULL or empty or pFileHandle is NULL @retval EFI_PROTOCOL_ERROR if there is no EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **/ EFI_STATUS OpenFileText( IN CHAR16 *pArgFilePath, OUT EFI_FILE_HANDLE *pFileHandle, IN CONST CHAR16 *pCurrentDirectory OPTIONAL, IN BOOLEAN CreateFileFlag ) { return OpenFileWithFlag(pArgFilePath, pFileHandle, pCurrentDirectory, CreateFileFlag, FALSE); } /** Open file or create new file in binary mode. @param[in] pArgFilePath path to a file that will be opened @param[out] pFileHandle output handler @param[in, optional] pCurrentDirectory is the current directory path to where we should start to search for the file. @param[in] CreateFileFlag TRUE to create new file or FALSE to open existing file @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER pFilePath is NULL or empty or pFileHandle is NULL @retval EFI_PROTOCOL_ERROR if there is no EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **/ EFI_STATUS OpenFileBinary( IN CHAR16 *pArgFilePath, OUT EFI_FILE_HANDLE *pFileHandle, IN CONST CHAR16 *pCurrentDirectory OPTIONAL, IN BOOLEAN CreateFileFlag ) { #ifdef OS_BUILD #ifdef _MSC_VER return OpenFileWithFlag(pArgFilePath, pFileHandle, pCurrentDirectory, CreateFileFlag, TRUE); #endif #endif return OpenFileWithFlag(pArgFilePath, pFileHandle, pCurrentDirectory, CreateFileFlag, FALSE); } /** Open file or create new file with the proper flags. @param[in] pArgFilePath path to a file that will be opened @param[out] pFileHandle output handler @param[in, optional] pCurrentDirectory is the current directory path to where we should start to search for the file. @param[in] CreateFileFlag - TRUE to create new file or FALSE to open existing file @param[in] binary - use binary open @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER pFilePath is NULL or empty or pFileHandle is NULL @retval EFI_PROTOCOL_ERROR if there is no EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **/ EFI_STATUS OpenFileWithFlag( IN CHAR16 *pArgFilePath, OUT EFI_FILE_HANDLE *pFileHandle, IN CONST CHAR16 *pCurrentDirectory OPTIONAL, IN BOOLEAN CreateFileFlag, BOOLEAN binary ) { EFI_STATUS Rc = EFI_SUCCESS; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *pVolume = NULL; CHAR16 *pFullFilePath = NULL; CHAR16 *pFilePath = NULL; EFI_FILE_HANDLE RootDirHandle = NULL; EFI_HANDLE *pHandles = NULL; UINTN HandlesSize = 0; INT32 Index = 0; UINTN CurrentWorkingDirLength = 0; NVDIMM_ENTRY(); /** @todo: This function have problem in two scenarios: 1) There are two or more file systems on platform. At least two of them contains file with provided name. There is no way to tell which one would be opened. It depends on enumeration order returned by LocateHandleBuffer. 2) Provided file name contains more than one dot (example: file.1.0.img), there is more than one file system on the platform and said file is not on the first one enumerated in pHandles returned by LocateHandleBuffer. In this case open and read succeed on each file system but data returned by read on those that do not contain said file is garbage. This is UDK issue. **/ if (pArgFilePath == NULL || pArgFilePath[0] == '\0' || pFileHandle == NULL) { Rc = EFI_INVALID_PARAMETER; goto Finish; } if (pCurrentDirectory != NULL && pArgFilePath[0] != '\\') { CurrentWorkingDirLength = StrLen(pCurrentDirectory); if (CurrentWorkingDirLength != 0 && pCurrentDirectory[CurrentWorkingDirLength - 1] != '\\') { pFullFilePath = CatSPrint(NULL, FORMAT_STR L"\\" FORMAT_STR, pCurrentDirectory, pArgFilePath); } else { pFullFilePath = CatSPrint(NULL, FORMAT_STR FORMAT_STR, pCurrentDirectory, pArgFilePath); } if (pFullFilePath != NULL) { pFilePath = pFullFilePath; while (pFilePath[0] != '\\' && pFilePath[0] != '\0') { pFilePath++; } } } Rc = gBS->LocateHandleBuffer(ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandlesSize, &pHandles); if (EFI_ERROR(Rc)) { NVDIMM_DBG("Couldn't find EfiSimpleFileSystemProtocol: " FORMAT_EFI_STATUS "", Rc); goto Finish; } if (pFilePath == NULL) { pFilePath = pArgFilePath; } for (Index = 0; Index < HandlesSize; Index++) { /** Get the file system protocol **/ Rc = gBS->OpenProtocol( pHandles[Index], &gEfiSimpleFileSystemProtocolGuid, (VOID *) &pVolume, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR(Rc)) { goto AfterHandles; } /** Open the root file **/ Rc = pVolume->OpenVolume(pVolume, &RootDirHandle); if (EFI_ERROR(Rc)) { goto AfterHandles; } if (CreateFileFlag) { // if EFI_FILE_MODE_CREATE then also EFI_FILE_MODE_READ and EFI_FILE_MODE_WRITE are needed. if (binary) { Rc = RootDirHandle->Open(RootDirHandle, pFileHandle, pFilePath, EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_BINARY, 0); } else { Rc = RootDirHandle->Open(RootDirHandle, pFileHandle, pFilePath, EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); } } else { if (binary) { Rc = RootDirHandle->Open(RootDirHandle, pFileHandle, pFilePath, EFI_FILE_MODE_READ | EFI_FILE_MODE_BINARY, 0); } else { Rc = RootDirHandle->Open(RootDirHandle, pFileHandle, pFilePath, EFI_FILE_MODE_READ, 0); } } RootDirHandle->Close(RootDirHandle); if (!EFI_ERROR(Rc)) { break; } } AfterHandles: FreePool(pHandles); Finish: if (pFullFilePath != NULL) { FreePool(pFullFilePath); } NVDIMM_EXIT_I64(Rc); return Rc; } /** Open file handle of root directory from given path @param[in] pDevicePath - path to file @param[out] pFileHandle - root directory file handle **/ EFI_STATUS OpenRootFileVolume( IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath, OUT EFI_FILE_HANDLE *pRootDirHandle ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_HANDLE DeviceHandle = NULL; EFI_DEVICE_PATH_PROTOCOL *pDevicePathTmp = NULL; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *pVolume = NULL; if (pDevicePath == NULL || pRootDirHandle == NULL) { goto Finish; } // Copy device path pointer, LocateDevicePath modifies it pDevicePathTmp = pDevicePath; // Locate Handle for Simple File System Protocol on device ReturnCode = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &pDevicePathTmp, &DeviceHandle); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = gBS->OpenProtocol(DeviceHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID *) &pVolume, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(ReturnCode)) { goto Finish; } // Open the root file ReturnCode = pVolume->OpenVolume(pVolume, pRootDirHandle); if (EFI_ERROR(ReturnCode)) { goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Open file or create new file based on device path protocol. @param[in] pArgFilePath Pointer to path to a file that will be opened @param[in] pDevicePath Pointer to instance of device path protocol @param[in] CreateFileFlag - TRUE to create new file or FALSE to open existing file @param[out] pFileHandle Output file handler @retval EFI_SUCCESS File opened or created @retval EFI_INVALID_PARAMETER Input parameter is invalid @retval Others From LocateDevicePath, OpenProtocol, OpenVolume and Open **/ EFI_STATUS OpenFileByDevice( IN CHAR16 *pArgFilePath, IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath, IN BOOLEAN CreateFileFlag, OUT EFI_FILE_HANDLE *pFileHandle ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_FILE_HANDLE RootDirHandle = NULL; NVDIMM_ENTRY(); if (pArgFilePath == NULL || pDevicePath == NULL || pFileHandle == NULL) { goto Finish; } ReturnCode = OpenRootFileVolume(pDevicePath, &RootDirHandle); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (CreateFileFlag) { // if EFI_FILE_MODE_CREATE then also EFI_FILE_MODE_READ and EFI_FILE_MODE_WRITE are needed. ReturnCode = RootDirHandle->Open(RootDirHandle, pFileHandle, pArgFilePath, EFI_FILE_MODE_CREATE|EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0); } else { ReturnCode = RootDirHandle->Open(RootDirHandle, pFileHandle, pArgFilePath, EFI_FILE_MODE_READ, 0); } if (EFI_ERROR(ReturnCode)) { goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Returns the size of the specified file. @param[in] FileHandle - handle to the opened file that we want to get the size for. @param[out] pFileSize - the result file size on bytes. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER if one of the input parameters is a NULL. Other return values associated with the GetInfo callback. **/ EFI_STATUS GetFileSize( IN EFI_FILE_HANDLE FileHandle, OUT UINT64 *pFileSize ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT64 BuffSize = 0; EFI_FILE_INFO *pFileInfo = NULL; if (FileHandle == NULL || pFileSize == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pFileSize = 0; ReturnCode = FileHandle->GetInfo(FileHandle, &gEfiFileInfoGuid, &BuffSize, pFileInfo); if (ReturnCode != EFI_BUFFER_TOO_SMALL) { NVDIMM_DBG("pFileHandle->GetInfo returned: " FORMAT_EFI_STATUS ".\n", ReturnCode); goto Finish; } pFileInfo = AllocatePool(BuffSize); if (pFileInfo == NULL) { NVDIMM_DBG("Could not allocate resources.\n"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = FileHandle->GetInfo(FileHandle, &gEfiFileInfoGuid, &BuffSize, pFileInfo); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("pFileHandle->GetInfo returned: " FORMAT_EFI_STATUS ".\n", ReturnCode); } *pFileSize = pFileInfo->FileSize; FreePool(pFileInfo); Finish: return ReturnCode; } /** Checks if the user-inputted desired ARS status matches with the current system-wide ARS status. @param[in] DesiredARSStatus Desired value of the ARS status to match against @param[out] pARSStatusMatched Pointer to a boolean value which shows if the current system ARS status matches the desired one. @retval EFI_SUCCESS if there were no problems @retval EFI_INVALID_PARAMETER if one of the input parameters is a NULL, or an invalid value. **/ EFI_STATUS MatchCurrentARSStatus( IN UINT8 DesiredARSStatus, OUT BOOLEAN *pARSStatusMatched ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; UINT8 CurrentARSStatus = LONG_OP_STATUS_NOT_STARTED; NVDIMM_ENTRY(); if ((pARSStatusMatched == NULL)|| ((DesiredARSStatus != LONG_OP_STATUS_UNKNOWN) && (DesiredARSStatus != LONG_OP_STATUS_NOT_STARTED) && (DesiredARSStatus != LONG_OP_STATUS_IN_PROGRESS) && (DesiredARSStatus != LONG_OP_STATUS_COMPLETED) && (DesiredARSStatus != LONG_OP_STATUS_ABORTED) && (DesiredARSStatus != LONG_OP_STATUS_ERROR))) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pARSStatusMatched = FALSE; ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **) &pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetARSStatus( pNvmDimmConfigProtocol, &CurrentARSStatus ); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed to get the current ARS status of the system"); goto Finish; } if (CurrentARSStatus == DesiredARSStatus) { *pARSStatusMatched = TRUE; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Poll long operation status Polls the status of the background operation on the dimm. @param [in] pNvmDimmConfigProtocol Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param [in] DimmId Dimm ID of the dimm to poll status @param [in] OpcodeToPoll Specify an opcode to poll, 0 to poll regardless of opcode @param [in] SubOpcodeToPoll Specify an opcode to poll @param [in] Timeout for the background operation **/ EFI_STATUS PollLongOpStatus( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN UINT16 DimmId, IN UINT8 OpcodeToPoll OPTIONAL, IN UINT8 SubOpcodeToPoll OPTIONAL, IN UINT64 Timeout ) { UINT8 EventCount = 0; EFI_STATUS ReturnCode = EFI_DEVICE_ERROR; UINT64 WaitIndex = 0; EFI_EVENT WaitList[2]; EFI_STATUS LongOpEfiStatus = EFI_NOT_READY; UINT8 CmdOpcode = 0; UINT8 CmdSubOpcode = 0; ZeroMem(WaitList, sizeof(WaitList)); gBS->CreateEvent(EVT_TIMER, TPL_NOTIFY, NULL, NULL, &WaitList[LONG_OP_POLL_EVENT_TIMER]); gBS->SetTimer(WaitList[LONG_OP_POLL_EVENT_TIMER], TimerPeriodic, LONG_OP_POLL_TIMER_INTERVAL); EventCount++; if (Timeout > 0) { gBS->CreateEvent(EVT_TIMER, TPL_NOTIFY, NULL, NULL, &WaitList[LONG_OP_POLL_EVENT_TIMEOUT]); gBS->SetTimer(WaitList[LONG_OP_POLL_EVENT_TIMEOUT], TimerRelative, Timeout); EventCount++; } do { ReturnCode = gBS->WaitForEvent(EventCount, WaitList, &WaitIndex); if (EFI_ERROR(ReturnCode)) { break; } ReturnCode = pNvmDimmConfigProtocol->GetLongOpStatus(pNvmDimmConfigProtocol, DimmId, &CmdOpcode, &CmdSubOpcode, NULL, NULL, &LongOpEfiStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Could not get long operation status"); goto Finish; } // If user passed in an opcode, validate that it matches the long operation opcode // If it doesn't match, assume it is < FIS 1.6 and not supported if (OpcodeToPoll != 0) { if (OpcodeToPoll != CmdOpcode || SubOpcodeToPoll != CmdSubOpcode) { ReturnCode = EFI_INCOMPATIBLE_VERSION; goto Finish; } } // Report back failure with the long op command if (EFI_ERROR(LongOpEfiStatus)) { if (LongOpEfiStatus != EFI_NO_RESPONSE) { ReturnCode = LongOpEfiStatus; goto Finish; } } if (WaitIndex == LONG_OP_POLL_EVENT_TIMEOUT) { NVDIMM_DBG("Timed out polling long operation status"); ReturnCode = EFI_TIMEOUT; goto Finish; } } while (LongOpEfiStatus != EFI_SUCCESS); ReturnCode = EFI_SUCCESS; Finish: gBS->CloseEvent(WaitList[LONG_OP_POLL_EVENT_TIMER]); if (Timeout > 0) { gBS->CloseEvent(WaitList[LONG_OP_POLL_EVENT_TIMEOUT]); } return ReturnCode; } /** Read file to given buffer * WARNING * caller is responsible for freeing ppFileBuffer @param[in] pFilePath - file path @param[in] pDevicePath - handle to obtain generic path/location information concerning the physical device or logical device. The device path describes the location of the device the handle is for. @param[in] MaxFileSize - if file is bigger skip read and return error @param[in] AsBinary - To open file as a binary file or not. (Windows will replace \r\n with \n if not binary file) @param[out] pFileSize - number of bytes written to buffer @param[out] ppFileBuffer - output buffer * WARNING * caller is responsible for freeing @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_NOT_STARTED Test was not executed @retval EFI_OUT_OF_RESOURCES if memory allocation fails. @retval EFI_SUCCESS All Ok **/ EFI_STATUS FileRead( IN CHAR16 *pFilePath, IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath, IN CONST UINT64 MaxFileSize, IN BOOLEAN AsBinary, OUT UINT64 *pFileSize, OUT VOID **ppFileBuffer ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_FILE_HANDLE pFileHandle = NULL; if (pFileSize == NULL || ppFileBuffer == NULL || pFilePath == NULL) { goto Finish; } #ifdef OS_BUILD ReturnCode = OpenFileWithFlag(pFilePath, &pFileHandle, NULL, 0, AsBinary); if (EFI_ERROR(ReturnCode) || pFileHandle == NULL) { NVDIMM_DBG("Failed opening File (" FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } #else if (pDevicePath == NULL) { goto Finish; } ReturnCode = OpenFileByDevice(pFilePath, pDevicePath, FALSE, &pFileHandle); if (EFI_ERROR(ReturnCode) || pFileHandle == NULL) { NVDIMM_DBG("Failed opening File (" FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } #endif ReturnCode = GetFileSize(pFileHandle, pFileSize); if (EFI_ERROR(ReturnCode) || *pFileSize == 0) { goto Finish; } if (MaxFileSize != 0 && *pFileSize > MaxFileSize) { ReturnCode = EFI_BUFFER_TOO_SMALL; goto Finish; } *ppFileBuffer = AllocateZeroPool(*pFileSize); if (*ppFileBuffer == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = pFileHandle->Read(pFileHandle, pFileSize, *ppFileBuffer); if (EFI_ERROR(ReturnCode)) { goto FinishFreeBuffer; } // Everything went fine, do not free buffer goto Finish; FinishFreeBuffer: FREE_POOL_SAFE(*ppFileBuffer); Finish: if (pFileHandle != NULL) { pFileHandle->Close(pFileHandle); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Read ASCII line from a file. The function ignores carriage return chars. @param FileHandle handle to a file @param pLine output buffer that will be filled with read line @param LineSize size of pLine buffer @param pEndOfFile output variable to report about end of file @retval EFI_SUCCESS @retval EFI_BUFFER_TOO_SMALL when pLine buffer is too small @retval EFI_INVALID_PARAMETER pLine or pEndOfFile is NULL **/ EFI_STATUS ReadAsciiLineFromFile( IN EFI_FILE_HANDLE FileHandle, OUT CHAR8 *pLine, IN INT32 LineSize, OUT BOOLEAN *pEndOfFile ) { EFI_STATUS Rc = EFI_SUCCESS; CHAR8 Buffer = 0; UINTN BufferSizeInBytes = sizeof(Buffer); INT32 Index = 0; NVDIMM_ENTRY(); if (pLine == NULL || pEndOfFile == NULL) { Rc = EFI_INVALID_PARAMETER; goto Finish; } if (LineSize == 0) { Rc = EFI_BUFFER_TOO_SMALL; goto Finish; } while (!EFI_ERROR(Rc = FileHandle->Read(FileHandle, &BufferSizeInBytes, &Buffer))) { // End of file if (BufferSizeInBytes == 0) { *pEndOfFile = TRUE; break; } // Ignore Carriage Return if (Buffer == '\r') { continue; } // End of line if (Buffer == '\n') { break; } // We need to have one CHAR8 reserved for the end of string '\0' if (Index + 1 >= LineSize) { Rc = EFI_BUFFER_TOO_SMALL; goto Finish; } pLine[Index] = Buffer; Index++; } if (EFI_ERROR(Rc)) { NVDIMM_DBG("Error reading the file: " FORMAT_EFI_STATUS "", Rc); } pLine[Index] = '\0'; Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** The Print function is not able to print long strings. This function is dividing the input string to safe lengths and prints all of the parts. @param[in] pString - the Unicode string to be printed **/ VOID LongPrint( IN CHAR16 *pString ) { UINT32 StrOffset = 0; UINT32 MaxToPrint = PCD_UEFI_LIB_MAX_PRINT_BUFFER_SIZE; CHAR16 TempChar = L'\0'; if (pString == NULL) { return; } while (pString[0] != L'\0') { while (pString[StrOffset] != L'\0' && StrOffset < MaxToPrint) { StrOffset++; } if (StrOffset == MaxToPrint) { TempChar = pString[StrOffset]; // Remember it and put a NULL there pString[StrOffset] = L'\0'; Print(FORMAT_STR, pString); // Print the string up to the newline pString[StrOffset] = TempChar;// Put back the stored value. pString += StrOffset; // Move the pointer over the printed part and the '\n' StrOffset = 0; } else { // There is a NULL after the newline or there is just a NULL Print(FORMAT_STR, pString); break; } } } /** Get basic information about the host server @param[out] pHostServerInfo pointer to a HOST_SERVER_INFO struct @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS GetHostServerInfo( OUT HOST_SERVER_INFO *pHostServerInfo ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); #ifdef OS_BUILD char name[HOST_SERVER_NAME_LEN]; char osName[HOST_SERVER_OS_NAME_LEN]; char osVersion[HOST_SERVER_OS_VERSION_LEN]; if (NULL == pHostServerInfo) { goto Finish; } if (0 != os_get_host_name(name, HOST_SERVER_NAME_LEN)) { goto Finish; } else { AsciiStrToUnicodeStrS(name, pHostServerInfo->Name, HOST_SERVER_NAME_LEN); } if (0 != os_get_os_name(osName, HOST_SERVER_OS_NAME_LEN)) { goto Finish; } else { AsciiStrToUnicodeStrS(osName, pHostServerInfo->OsName, HOST_SERVER_OS_NAME_LEN); } if (0 != os_get_os_version(osVersion, HOST_SERVER_OS_VERSION_LEN)) { goto Finish; } else { AsciiStrToUnicodeStrS(osVersion, pHostServerInfo->OsVersion, HOST_SERVER_OS_VERSION_LEN); } ReturnCode = EFI_SUCCESS; #else //2nd arg is size of the destination buffer in bytes so sizeof is appropriate UnicodeSPrint(pHostServerInfo->OsName, sizeof(pHostServerInfo->OsName), L"UEFI"); UnicodeSPrint(pHostServerInfo->Name, sizeof(pHostServerInfo->Name), L"N/A"); UnicodeSPrint(pHostServerInfo->OsVersion, sizeof(pHostServerInfo->OsVersion), L"N/A"); ReturnCode = EFI_SUCCESS; goto Finish; #endif Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieves Intel Dimm Config EFI vars User is responsible for freeing ppIntelDIMMConfig @param[out] pIntelDIMMConfig Pointer to struct to fill with EFI vars @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS RetrieveIntelDIMMConfig( OUT INTEL_DIMM_CONFIG **ppIntelDIMMConfig ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINTN VariableSize = 0; NVDIMM_ENTRY(); *ppIntelDIMMConfig = AllocateZeroPool(sizeof(INTEL_DIMM_CONFIG)); if (*ppIntelDIMMConfig == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } VariableSize = sizeof(INTEL_DIMM_CONFIG); ReturnCode = GET_VARIABLE( INTEL_DIMM_CONFIG_VARIABLE_NAME, gIntelDimmConfigVariableGuid, &VariableSize, *ppIntelDIMMConfig); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Could not find IntelDIMMConfigs"); FREE_POOL_SAFE(*ppIntelDIMMConfig); goto Finish; } NVDIMM_DBG("Revision: %d", (*ppIntelDIMMConfig)->Revision); NVDIMM_DBG("ProvisionCapacityMode: %d", (*ppIntelDIMMConfig)->ProvisionCapacityMode); NVDIMM_DBG("MemorySize: %d", (*ppIntelDIMMConfig)->MemorySize); NVDIMM_DBG("PMType: %d", (*ppIntelDIMMConfig)->PMType); NVDIMM_DBG("ProvisionNamespaceMode: %d", (*ppIntelDIMMConfig)->ProvisionNamespaceMode); NVDIMM_DBG("NamespaceFlags: %d", (*ppIntelDIMMConfig)->NamespaceFlags); NVDIMM_DBG("ProvisionCapacityStatus: %d", (*ppIntelDIMMConfig)->ProvisionCapacityStatus); NVDIMM_DBG("ProvisionNamespaceStatus: %d", (*ppIntelDIMMConfig)->ProvisionNamespaceStatus); NVDIMM_DBG("NamespaceLabelVersion: %d", (*ppIntelDIMMConfig)->NamespaceLabelVersion); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #ifndef OS_BUILD #define EFI_ACPI_16550_UART_HID EISA_PNP_ID(0x0501) extern EFI_GUID gEfiSerialIoProtocolGuid; /** Check whether the device path node is ISA Serial Node. @param[in] Acpi Device path node to be checked @retval TRUE It is ISA Serial Node. @retval FALSE It is NOT ISA Serial Node. **/ BOOLEAN IsISASerialNode( IN ACPI_HID_DEVICE_PATH *Acpi ) { return (BOOLEAN)( (DevicePathType(Acpi) == ACPI_DEVICE_PATH) && (DevicePathSubType(Acpi) == ACPI_DP) && (ReadUnaligned32(&Acpi->HID) == EFI_ACPI_16550_UART_HID) ); } /** The initialization routine in DebugLib initializes the serial port to a static value defined in module dec file. This function find's out the serial port attributes from SerialIO protocol and set it on serial port @retval EFI_SUCCESS The function complete successfully. @retval EFI_UNSUPPORTED No serial ports present. **/ EFI_STATUS SetSerialAttributes( VOID ) { UINTN Index; UINTN NoHandles; EFI_HANDLE *Handles; EFI_STATUS ReturnCode; ACPI_HID_DEVICE_PATH *Acpi; EFI_DEVICE_PATH_PROTOCOL *DevicePath; EFI_SERIAL_IO_PROTOCOL *SerialIo; EFI_DEVICE_PATH_PROTOCOL *Node; ReturnCode = gBS->LocateHandleBuffer( ByProtocol, &gEfiSerialIoProtocolGuid, NULL, &NoHandles, &Handles ); CHECK_RETURN_CODE(ReturnCode,Finish); for (Index = 0; Index < NoHandles; Index++) { // Check to see whether the handle has DevicePath Protocol installed ReturnCode = gBS->HandleProtocol( Handles[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath ); CHECK_RETURN_CODE(ReturnCode,Finish); Acpi = NULL; for (Node = DevicePath; !IsDevicePathEnd(Node); Node = NextDevicePathNode(Node)) { if ((DevicePathType(Node) == MESSAGING_DEVICE_PATH) && (DevicePathSubType(Node) == MSG_UART_DP)) { break; } // Acpi points to the node before Uart node Acpi = (ACPI_HID_DEVICE_PATH *)Node; } if ((Acpi != NULL) && IsISASerialNode(Acpi)) { ReturnCode = gBS->HandleProtocol( Handles[Index], &gEfiSerialIoProtocolGuid, (VOID **)&SerialIo ); CHECK_RETURN_CODE(ReturnCode,Finish); EFI_PARITY_TYPE Parity = (EFI_PARITY_TYPE)SerialIo->Mode->Parity; UINT8 DataBits = (UINT8)SerialIo->Mode->DataBits; EFI_STOP_BITS_TYPE StopBits = (EFI_STOP_BITS_TYPE)(SerialIo->Mode->StopBits); ReturnCode = SerialPortSetAttributes( &(SerialIo->Mode->BaudRate), &(SerialIo->Mode->ReceiveFifoDepth), &(SerialIo->Mode->Timeout), &Parity, &DataBits, &StopBits); CHECK_RETURN_CODE(ReturnCode,Finish); break; } } Finish: FREE_POOL_SAFE(Handles); return ReturnCode; } #endif #if defined(DYNAMIC_WA_ENABLE) /** Local define of the Shell Protocol GetEnv function. The local definition allows the driver to use this function without including the Shell headers. **/ typedef CONST CHAR16 * (EFIAPI *EFI_SHELL_GET_ENV_LOCAL) ( IN CONST CHAR16 *Name OPTIONAL ); /** Local, partial definition of the Shell Protocol, the first Reserved value is a pointer to a different function, but we don't need it here so it is masked as reserved. We ignore any functions after the GetEnv one. **/ typedef struct { VOID *Reserved; EFI_SHELL_GET_ENV_LOCAL GetEnv; } EFI_SHELL_PROTOCOL_GET_ENV; /** Returns the value of the environment variable with the given name. @param[in] pVarName Unicode name of the variable to retrieve @retval NULL if the shell protocol could not be located or if the variable is not defined in the system @retval pointer to the Unicode string containing the variable value **/ CHAR16 * GetEnvVariable( IN CHAR16 *pVarName ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 *pReturn = NULL; EFI_SHELL_PROTOCOL_GET_ENV *pGetEnvShell = NULL; EFI_GUID GetEnvShellProtGuid = EFI_SHELL_PROTOCOL_GUID; ReturnCode = gBS->LocateProtocol(&GetEnvShellProtGuid, NULL, (VOID **)&pGetEnvShell); if (!EFI_ERROR(ReturnCode)) { pReturn = (CHAR16 *)pGetEnvShell->GetEnv(pVarName); } return pReturn; } #endif //Its ok to keep these routines here, but they should be calling abstracted serialize/deserialize data APIs in the future. extern EFI_GUID gIntelDimmPbrTagIdVariableguid; #ifndef OS_BUILD EFI_STATUS PbrDcpmmSerializeTagId( UINT32 Id ) { UINTN VariableSize; EFI_STATUS ReturnCode = EFI_SUCCESS; VariableSize = sizeof(UINT32); ReturnCode = SET_VARIABLE( PBR_TAG_ID_VAR, gIntelDimmPbrTagIdVariableguid, VariableSize, (VOID*)&Id); return ReturnCode; } EFI_STATUS PbrDcpmmDeserializeTagId( UINT32 *pId, UINT32 DefaultId ) { UINTN VariableSize; EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Id = 0; VariableSize = sizeof(UINT32); ReturnCode = GET_VARIABLE( PBR_TAG_ID_VAR, gIntelDimmPbrTagIdVariableguid, &VariableSize, &Id); if (ReturnCode == EFI_NOT_FOUND) { *pId = DefaultId; ReturnCode = EFI_SUCCESS; goto Finish; } else if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to retrieve PBR TAG ID value"); goto Finish; } *pId = Id; Finish: return ReturnCode; } #else #include #ifdef _MSC_VER extern int registry_volatile_write(const char *key, unsigned int dword_val); extern int registry_read(const char *key, unsigned int *dword_val, unsigned int default_val); #else #include #include #include #endif /** Helper that serializes pbr id to a volatile store. We should not be maintaining sessions across system reboots **/ EFI_STATUS PbrDcpmmSerializeTagId( UINT32 id ) { EFI_STATUS ReturnCode = EFI_LOAD_ERROR; #if _MSC_VER registry_volatile_write("pbr_id", id); #else UINT32 ShmId; key_t Key; UINT32 *pPbrId = NULL; Key = ftok(PBR_TMP_DIR, 'i'); ShmId = shmget(Key, sizeof(*pPbrId), IPC_CREAT | 0666); if (-1 == ShmId) { NVDIMM_DBG("Failed to shmget\n"); return ReturnCode; } pPbrId = (UINT32*)shmat(ShmId, NULL, 0); if ((VOID*)pPbrId == (VOID*)-1) { NVDIMM_DBG("Failed to shmat\n"); return ReturnCode; } else { *pPbrId = id; NVDIMM_DBG("Writing to shared memory: %d\n", *pPbrId); shmdt(pPbrId); //If id is reset to zero it is ok to mark //this share memory to be removed if (0 == id) { shmctl(ShmId, IPC_RMID, NULL); } ReturnCode = EFI_SUCCESS; } #endif return ReturnCode; } /** Helper that deserializes pbr tag id from a volatile store. We should not be maintaining sessions across system reboots **/ EFI_STATUS PbrDcpmmDeserializeTagId( UINT32 *id, UINT32 defaultId ) { EFI_STATUS ReturnCode = EFI_LOAD_ERROR; #if _MSC_VER registry_read("pbr_id", id, defaultId); #else UINT32 ShmId; key_t Key; UINT32 *pPbrId = NULL; Key = ftok(PBR_TMP_DIR, 'i'); ShmId = shmget(Key, sizeof(*pPbrId), IPC_CREAT | 0666); if (-1 == ShmId) { NVDIMM_DBG("Failed to shmget\n"); return ReturnCode; } ReturnCode = EFI_SUCCESS; pPbrId = (UINT32*)shmat(ShmId, NULL, 0); if ((VOID*)pPbrId == (VOID*)-1) { NVDIMM_DBG("Failed to shmat\n"); *id = defaultId; } else { *id = *pPbrId; shmdt(pPbrId); //If id is reset to zero it is ok to mark //this share memory to be removed if (0 == *id) { shmctl(ShmId, IPC_RMID, NULL); } } #endif return ReturnCode; } #endif /** Function that allows for nicely formatted HEX & ASCII debug output. It can be used to inspect memory objects without a need for debugger @param[in] pBuffer Pointer to an arbitrary object @param[in] Bytes Number of bytes to display **/ #define COLUMN_IN_HEX_DUMP 16 VOID HexDebugPrint( IN VOID *pBuffer, IN UINT32 Bytes ) { UINT8 Byte, AsciiBuffer[COLUMN_IN_HEX_DUMP]; UINT16 Column, NextColumn, Index, Index2; UINT8 *pData; if (pBuffer == NULL) { NVDIMM_DBG("pBuffer is NULL"); return; } DebugPrint(EFI_D_INFO, "Hexdump starting at: 0x%p\n", pBuffer); pData = (UINT8 *) pBuffer; for (Index = 0; Index < Bytes; Index++) { Column = Index % COLUMN_IN_HEX_DUMP; NextColumn = (Index + 1) % COLUMN_IN_HEX_DUMP; Byte = *(pData + Index); if (Column == 0) { DebugPrint(EFI_D_INFO, "%.3d:", Index); } if (Index % 8 == 0) { DebugPrint(EFI_D_INFO, " "); } DebugPrint(EFI_D_INFO, "%.2x", *(pData + Index)); AsciiBuffer[Column] = IsAsciiAlnumCharacter(Byte) ? Byte : '.'; if (NextColumn == 0 && Index != 0) { DebugPrint(EFI_D_INFO, " "); for (Index2 = 0; Index2 < COLUMN_IN_HEX_DUMP; Index2++) { DebugPrint(EFI_D_INFO, "%c", AsciiBuffer[Index2]); if (Index2 == COLUMN_IN_HEX_DUMP / 2 - 1) { DebugPrint(EFI_D_INFO, " "); } } DebugPrint(EFI_D_INFO, "\n"); } } } /** Function that allows for nicely formatted HEX & ASCII console output. It can be used to inspect memory objects without a need for debugger or dumping raw DIMM data. @param[in] pBuffer Pointer to an arbitrary object @param[in] Bytes Number of bytes to display **/ VOID HexPrint( IN VOID *pBuffer, IN UINT32 Bytes ) { UINT8 Byte, AsciiBuffer[COLUMN_IN_HEX_DUMP]; UINT16 Column, NextColumn, Index, Index2; UINT8 *pData; if (pBuffer == NULL) { NVDIMM_DBG("pBuffer is NULL"); return; } Print(L"Hexdump for %d bytes:\n", Bytes); pData = (UINT8 *)pBuffer; for (Index = 0; Index < Bytes; Index++) { Column = Index % COLUMN_IN_HEX_DUMP; NextColumn = (Index + 1) % COLUMN_IN_HEX_DUMP; Byte = *(pData + Index); if (Column == 0) { Print(L"%.3d:", Index); } if (Index % 8 == 0) { Print(L" "); } Print(L"%.2x", *(pData + Index)); AsciiBuffer[Column] = IsAsciiAlnumCharacter(Byte) ? Byte : '.'; if (NextColumn == 0 && Index != 0) { Print(L" "); for (Index2 = 0; Index2 < COLUMN_IN_HEX_DUMP; Index2++) { Print(L"%c", AsciiBuffer[Index2]); if (Index2 == COLUMN_IN_HEX_DUMP / 2 - 1) { Print(L" "); } } Print(L"\n"); } } } ipmctl-03.00.00.0485/DcpmPkg/common/Version.h000066400000000000000000000022711440615110200202300ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /** Version number is BCD with format: MMmmRr M = Major version number m = Minor version number R = Release type (Alpha, Beta, Test, etc) r = Release version **/ #ifndef _VERSION_H_ #define _VERSION_H_ #define VENDOR_ID 0x8086 #define PRODUCT_NAME L"Intel(R) Optane(TM) Persistent Memory" #define GENERAL_RELEASE 0 #define ALPHA_RELEASE 1 #define BETA_RELEASE 2 #define TEST_RELEASE 3 #define NVMDIMM_MAJOR_VERSION 3 #define NVMDIMM_MINOR_VERSION 0 #define NVMDIMM_MAJOR_API_VERSION 1 #define NVMDIMM_MINOR_API_VERSION 1 #ifdef OS_BUILD #include #else #include #endif #define STRING_TO_WIDE(A, B) A##B #define WIDEN_UP_STRING(A) STRING_TO_WIDE(L,#A) #define DEFINE_TO_STRING(A) WIDEN_UP_STRING(A) #define STRINGIZE2(s) #s #define STRINGIZE(s) STRINGIZE2(s) #define NVMD_CONFIG_PROTOCOL_VERSION (UINT32)(((NVMDIMM_RELEASE_TYPE == TEST_RELEASE ? 1 : 0) << 31 | \ (NVMDIMM_MINOR_VERSION & 0x7FFF) << 16 | (NVMDIMM_MAJOR_VERSION & 0xFFFF))) #endif /** _VERSION_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/common/Version_OS_build.h000066400000000000000000000007051440615110200220100ustar00rootroot00000000000000/* * Copyright (c) 2021, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _VERSION_OS_BUILD_H_ #define _VERSION_OS_BUILD_H_ #define NVMDIMM_RELEASE_TYPE GENERAL_RELEASE #define NVMDIMM_VERSION NVMDIMM_MAJOR_VERSION<<16 | \ NVMDIMM_MINOR_VERSION<<8 #define NVMDIMM_VERSION_STRING DEFINE_TO_STRING(__VERSION_NUMBER__) #define NVMDIMM_VERSION_STRING_A STRINGIZE(__VERSION_NUMBER__) #endif /** _VERSION_OS_BUILD_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/driver/000077500000000000000000000000001440615110200164335ustar00rootroot00000000000000ipmctl-03.00.00.0485/DcpmPkg/driver/Core/000077500000000000000000000000001440615110200173235ustar00rootroot00000000000000ipmctl-03.00.00.0485/DcpmPkg/driver/Core/AsmCommands.h000066400000000000000000000104041440615110200216750ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _ASM_COMMANDS_H_ #define _ASM_COMMANDS_H_ #include "Utility.h" /** The software query the availability of the new instructions must be set to: CPUID.(EAX=07H, ECX=0H) **/ #define CPUID_NEWMEM_FUNCTIONS_EAX 0x7 #define CPUID_NEWMEM_FUNCTIONS_ECX 0x0 /** Sends the assembler CPUID command to the processor. This is the extended version, where the caller can also specify the initial value of the ECX register. @param[in, out] pCpuInfo array of four 32bit integers, where the result will be stored. Their value also takes part in the command parsing. @param[in] InfoType the main parameter that defines what "page" of CPU capabilities the caller wants to retrieve. @param[in] EcxValue the initial value of the ECX register just before calling the CPUID processor command. @retval - nothing, the result data will be stored int the CpuInfo array. Warning! The InfoType parameter can go outside of the processor supported pages, resulting with a CPU exception. It is the caller responsibility to be sure that the processor supports the requested parameter or that the user performs proper checking before issuing the command. **/ VOID EFIAPI AsmCpuidEcx( IN UINT32 RegisterInEax, IN UINT32 RegisterInEcx, OUT UINT32 *pRegisterOutEax, OUT UINT32 *pRegisterOutEbx, OUT UINT32 *pRegisterOutEcx, OUT UINT32 *pRegisterOutEdx ); /** Performs a serializing operation on all store-to-memory instructions that were issued prior the SFENCE instruction. The function is defined here, because the used GCC does not yet have it defined. **/ VOID AsmSfence( ); /** Flushes a cache line from all the instruction and data caches within the coherency domain of the CPU. This is one of the faster flushing function available, it is preferred to use it if the processor does not support any faster flush functions. Flushed the cache line specified by LinearAddress. @param[in] LinearAddress The address of the cache line to flush. **/ VOID AsmClFlushOpt( IN VOID *pLinearAddress ); /** Flushes a cache line from all the instruction and data caches within the coherency domain of the CPU. This is currently the fastest flushing function available, it is preferred to use it if the processor supports it. Flushed the cache line specified by LinearAddress. @param[in] LinearAddress The address of the cache line to flush. **/ VOID AsmClWb( IN VOID *pLinearAddress ); /** Flushes a cache line from all the instruction and data caches within the coherency domain of the CPU. This is one of the first flushing function available, it is the slowest so it is preferred to use only if the processor does not support any other flush functions. Flushed the cache line specified by LinearAddress. @param[in] LinearAddress The address of the cache line to flush. **/ VOID AsmFlushCl( IN VOID *LinearAddress ); /** Loads the pSrc buffer into XMM register and does a non-temporal copy of 128 bits to the pDest buffer. No buffer validation takes place here. @param[in] pDest The destination buffer, needs to have at least 16 bytes. Needs to be aligned to 16 bytes. @param[in] pSrc The source buffer, needs to have at least 16 bytes. No alignment required. **/ VOID AsmNonTemporalStore128( IN VOID *pDest, OUT VOID *pSrc ); /** Loads the pSrc buffer into YMM register and does a non-temporal copy of 256 bits to the pDest buffer. No buffer validation takes place here. @param [in] pDest The destination buffer, needs to have at least 32 bytes. Needs to be aligned to 32 bytes. @param [in] pSrc The source buffer, needs to have at least 32 bytes. No alignment required. **/ VOID AsmNonTemporalStore256( IN VOID *pDest, OUT VOID *pSrc ); /** Loads the pSrc buffer into ZMM register and does a non-temporal copy of 512 bits to the pDest buffer. No buffer validation takes place here. @param [in] pDest The destination buffer, needs to have at least 64 bytes. Needs to be aligned to 64 bytes. @param [in] pSrc The source buffer, needs to have at least 64 bytes. No alignment required. **/ VOID AsmNonTemporalStore512( IN VOID *pDest, OUT VOID *pSrc ); #endif ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Btt.c000066400000000000000000001473251440615110200202340ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include "Btt.h" #include "BttLayout.h" #include "Namespace.h" #include GUID gBttAbstractionGuid = EFI_BTT_ABSTRACTION_GUID; /** Loads up a single flog pair @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [in] pArena Pointer to the Arena from which the Flog Pair is to be read @param [in] FlogOffset Offset to the specific Flog entry @param [out] pFlogRuntime Result of Flog read @param [in] FlogNum Number of Flog to be read **/ STATIC EFI_STATUS BttReadFlogPair( IN BTT *pBtt, IN ARENAS *pArena, IN UINT64 FlogOffset, OUT FLOG_RUNTIME *pFlogRuntime, IN UINT32 FlogNum ); /** Loads up all the flog Entries for an arena @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [in,out] pArena Pointer to the Arena from which the Flog Pairs are to be read **/ STATIC EFI_STATUS BttReadFlogs( IN BTT *pBtt, IN OUT ARENAS *pArena ); /** Writes out an updated Flog entry The Flog Entries are not checksummed. Instead, increasing Sequence numbers are used to atomically switch the active Flog entry between the first and second struct btt_Flog in each slot. In order for this to work, the Sequence number must be updated only after all the other fields in the Flog are updated. So the writes to the Flog are broken into two writes, one for the first three fields (lba, OldMap, NewMap) and, only after those fields are known to be written durably, the second write for the Seq field is done. @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [in,out] pArena Pointer to the Arena from which the Flog Pair is to be read @param [in] Lba Logical block address to be written @param [in] OldMap Previous map entry to be written @param [in] NewMap New map entry to be written **/ STATIC EFI_STATUS BttFlogUpdate( IN BTT *pBtt, IN ARENAS *pArena, IN UINT32 Lba, IN UINT32 OldMap, IN UINT32 NewMap ); /** Constructs a read tracking table for an arena The Rtt is big enough to hold an entry for each free block (NFree) @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [in,out] pArena Pointer to the Arena with Rtt to be created **/ STATIC EFI_STATUS BttBuildRtt( IN BTT *pBtt, IN OUT ARENAS *pArena ); /** Loads up an arena and build run-time state @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [in] ArenaOffset Offset to the Arena to be read @param [in,out] pArena Pointer to the Arena to be read **/ STATIC EFI_STATUS BttReadArena( IN BTT *pBtt, IN UINT64 ArenaOffset, IN OUT ARENAS *pArena ); /** Loads up all Arenas and builds run-time state On entry, layout must be known to be valid, and the number of Arenas must be known. @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [in] NArenas Number of arenas to be read **/ STATIC EFI_STATUS BttReadArenas( IN BTT *pBtt, IN UINT32 NArenas ); /** Loads up layout Info from btt namespace Called once when the btt namespace is opened for use. Sets pBtt->Laidout to 0 if no valid layout is found, 1 otherwise. Any recovery actions required (as indicated by the Flog state) are performed by this routine. Any quick checks for layout consistency are performed by this routine (quick enough to be done each time a BTT area is opened for use, not like the slow consistency checks done by BttCheck()). @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle **/ STATIC EFI_STATUS BttReadLayout( IN BTT *pBtt ); /* Satisfies a read with a block of zeros @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [out] Buffer Output buffer **/ STATIC EFI_STATUS BttZeroBlock( IN BTT *pBtt, OUT VOID *pBuffer ); /** Calculates the arena & pre-map LBA This routine takes the external LBA and matches it to the appropriate arena, adjusting the Lba for use within that arena. If successful, *pArena is a pointer to the appropriate arena struct in the run-time state, and *PreMapLba is the LBA adjusted to an arena-internal LBA (also known as the pre-map LBA). @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [in] Lba External LBA @param [out] pArena Pointer to the appropriate arena struct in the run-time state @param [out] PreMapLba LBA adjusted to an arena-internal LBA **/ STATIC EFI_STATUS BttLbaToArenaLba( IN BTT *pBtt, IN UINT64 Lba, OUT ARENAS **ppArena, OUT UINT32 *PreMapLba ); /** Performs a consistency check on an arena @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [in] pArena Pointer to the Arena to be checked **/ STATIC EFI_STATUS BttCheckArena( IN BTT *pBtt, IN ARENAS *pArena ); /** Verifies if Lba is invalid This function is used at the top of the entry points where an external LBA is provided, like this: if (!NT_SUCCESS (IsLbaValid(pBtt, Lba)))\n return STATUS_UNSUCCESSFUL; @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [in] Lba Logical block address **/ STATIC EFI_STATUS IsLbaValid( IN BTT *pBtt, IN UINT64 Lba ); BTT * BttInit( IN UINT64 RawSize, IN UINT32 LbaSize, IN GUID *pParentUuid, IN VOID *pNamespace ) { BTT *pBtt = NULL; NVDIMM_DBG("RawSize=%x LbaSize=%d", RawSize, LbaSize); if(RawSize < BTT_NAMESPACE_MIN_SIZE) { NVDIMM_DBG("RawSize smaller than BTT_MIN_SIZE %d", BTT_NAMESPACE_MIN_SIZE); return NULL; } pBtt = AllocatePool(sizeof(BTT)); if(pBtt == NULL) { NVDIMM_DBG("Memory allocation for %x bytes failed", sizeof(BTT)); return NULL; } ZeroMem(pBtt, sizeof(BTT)); CopyMem_S(&pBtt->ParentUuid, sizeof(pBtt->ParentUuid), pParentUuid, sizeof(GUID)); pBtt->RawSize = RawSize; pBtt->LbaSize = LbaSize; pBtt->pNamespace = pNamespace; if ((((NAMESPACE *) pNamespace)->Major == NSINDEX_MAJOR) && (((NAMESPACE *) pNamespace)->Minor == NSINDEX_MINOR_1)) { pBtt->PrimaryInfoOffset = BTT_PRIMARY_INFO_BLOCK_OFFSET_1_1; } else { pBtt->PrimaryInfoOffset = BTT_PRIMARY_INFO_BLOCK_OFFSET; } /** Load up layout, if it exists. Whether BttReadLayout() finds a valid layout or not, it finishes updating these layout-related fields: pBtt->NFree pBtt->NLbas pBtt->NArenas since these fields are used even before a valid layout it written. **/ if(EFI_ERROR(BttReadLayout(pBtt))) { BttRelease(pBtt); /* free up any allocations */ return NULL; } // Set blockcount to usable size, excluding metadata ((NAMESPACE *) pNamespace)->UsableSize = pBtt->NLbas * pBtt->LbaSize; NVDIMM_DBG("Success, pBtt=%p", pBtt); return pBtt; } STATIC EFI_STATUS IsLbaValid( IN BTT *pBtt, IN UINT64 Lba ) { if (pBtt == NULL) { return EFI_INVALID_PARAMETER; } if (Lba >= pBtt->NLbas) { NVDIMM_DBG("lba out of range(NLbas %lu)", pBtt->NLbas); return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; } EFI_STATUS BttReadInfo( IN BTT_INFO *pInfo, IN BTT *pBtt OPTIONAL ) { if (pInfo == NULL) { return EFI_INVALID_PARAMETER; } if (CompareMem(pInfo->Sig, Sig, BTTINFO_SIG_LEN) != 0) { NVDIMM_DBG("Invalid BTT signature "); return EFI_ABORTED; } if (pBtt != NULL) { if (CompareMem(&pInfo->ParentUuid, &pBtt->ParentUuid, sizeof(GUID)) != 0){ NVDIMM_DBG("parent UUID mismatch"); return EFI_ABORTED; } } /* to be valid, the fields must Checksum correctly */ if (!ChecksumOperations((VOID *)pInfo, sizeof(BTT_INFO), &pInfo->Checksum, FALSE)) { NVDIMM_DBG("Invalid checksum"); return EFI_ABORTED; } /* to be valid, Info block must have Major version of at least 1 */ if (pInfo->Major == 0) { NVDIMM_DBG("Invalid major version(0)"); return EFI_ABORTED; } return EFI_SUCCESS; } STATIC INLINE BOOLEAN MapEntryIsError( IN UINT32 MapEntry ) { return (MapEntry & ~BTT_MAP_ENTRY_LBA_MASK) == BTT_MAP_ENTRY_ERROR; } STATIC INLINE BOOLEAN MapEntryIsZero( IN UINT32 MapEntry ) { return (MapEntry & ~BTT_MAP_ENTRY_LBA_MASK) == BTT_MAP_ENTRY_ZERO; } STATIC INLINE BOOLEAN MapEntryIsInitial( IN UINT32 MapEntry ) { return (MapEntry & ~BTT_MAP_ENTRY_LBA_MASK) == BTT_MAP_ENTRY_INITIAL; } STATIC EFI_STATUS BttReadFlogPair( IN BTT *pBtt, IN ARENAS *pArena, IN UINT64 FlogOffset, OUT FLOG_RUNTIME *pFlogRuntime, IN UINT32 FlogNum ) { BTT_FLOG_PAIR * pFlogPair = NULL; UINT8 CurrentFlogIndex = 0; UINT64 MapEntryOffset = 0; BTT_MAP_ENTRIES Entry; UINT8 CurrentMapPos = 0; UINT32 CurrentMap = 0; EFI_STATUS ReturnCode = EFI_SUCCESS; pFlogPair = &(pFlogRuntime->FlogPair); SetMem(pFlogPair, sizeof(BTT_FLOG_PAIR), 0x0); SetMem(&Entry, sizeof(Entry), 0x0); NVDIMM_VERB("pBtt=%p pArena=%p FlogOffset=%x pFlogRuntime=%p FlogNum=%d", pBtt, pArena, FlogOffset, pFlogRuntime, FlogNum); if(!pBtt || !pFlogRuntime) { return EFI_INVALID_PARAMETER; } pFlogRuntime->Entry = FlogOffset; if(FlogOffset == 0) { NVDIMM_DBG("invalid flog offset %llu", FlogOffset); return EFI_INVALID_PARAMETER; } ReturnCode = ReadNamespaceBytes( pBtt->pNamespace, FlogOffset, pFlogPair, sizeof(BTT_FLOG_PAIR) ); if(EFI_ERROR(ReturnCode)) { return ReturnCode; } if(EFI_ERROR(IsLbaValid(pBtt, pFlogPair->Flog[0].Lba)) || EFI_ERROR(IsLbaValid(pBtt, pFlogPair->Flog[1].Lba))) { return EFI_INVALID_PARAMETER; } NVDIMM_VERB("FlogPair[0] FlogOffset=%x OldMap=%d NewMap=%d Seq=%d", FlogOffset, pFlogPair->Flog[0].OldMap, pFlogPair->Flog[0].NewMap, pFlogPair->Flog[0].Seq); NVDIMM_VERB("FlogPair[1] OldMap=%d NewMap=%d Seq=%d", pFlogPair->Flog[1].OldMap, pFlogPair->Flog[1].NewMap, pFlogPair->Flog[1].Seq); /* Interesting cases: - no valid Seq numbers: layout consistency error - one valid Seq number: that's the current Entry - two valid Seq numbers: higher number is current Entry - identical Seq numbers: layout consistency error */ if (pFlogPair->Flog[0].Seq == pFlogPair->Flog[1].Seq) { NVDIMM_DBG("Flog layout error: bad Seq numbers %d %d\n", pFlogPair->Flog[0].Seq, pFlogPair->Flog[1].Seq); SET_BIT(&pArena->Flags, BTTINFO_FLAG_ERROR); ReturnCode = EFI_LOAD_ERROR; goto Finish; } else if (pFlogPair->Flog[0].Seq == 0) { /* singleton valid Flog at FlogPair[1] */ CurrentFlogIndex = 1; pFlogRuntime->Next = 0; } else if(pFlogPair->Flog[1].Seq == 0) { /* singleton valid Flog at FlogPair[0] */ CurrentFlogIndex = 0; pFlogRuntime->Next = 1; } else if(NSEQ(pFlogPair->Flog[0].Seq) == pFlogPair->Flog[1].Seq) { /* FlogPair[1] has the later Sequence number */ CurrentFlogIndex = 1; pFlogRuntime->Next = 0; } else if (pFlogPair->Flog[0].Seq == NSEQ(pFlogPair->Flog[1].Seq)) { /* FlogPair[0] has the later Sequence number */ CurrentFlogIndex = 0; pFlogRuntime->Next = 1; } else { NVDIMM_ERR("Flog layout error, not off by 1"); ReturnCode = EFI_LOAD_ERROR; goto Finish; } NVDIMM_VERB("run-time Flog Next is %d", pFlogRuntime->Next); NVDIMM_VERB("read Flog[%d]: Lba %d old %d%s%s new %d%s%s", FlogNum, pFlogPair->Flog[CurrentFlogIndex].Lba, pFlogPair->Flog[CurrentFlogIndex].OldMap & BTT_MAP_ENTRY_LBA_MASK, (pFlogPair->Flog[CurrentFlogIndex].OldMap & BTT_MAP_ENTRY_ERROR) ? " ERROR" : "", (pFlogPair->Flog[CurrentFlogIndex].OldMap & BTT_MAP_ENTRY_ZERO) ? " ZERO" : "", pFlogPair->Flog[CurrentFlogIndex].NewMap & BTT_MAP_ENTRY_LBA_MASK, (pFlogPair->Flog[CurrentFlogIndex].NewMap & BTT_MAP_ENTRY_ERROR) ? " ERROR" : "", (pFlogPair->Flog[CurrentFlogIndex].NewMap & BTT_MAP_ENTRY_ZERO) ? " ZERO" : ""); /* Decide if the current Flog Info represents a completed operation or an incomplete operation. If completed, the OldMap field will contain the free block to be used for the Next write. But if the operation didn't complete(indicated by the map Entry not being updated), then NewMap is the free block since it never became active according to the map. A special case, used by Flog Entries when first created, is when OldMap == NewMap. This Counts as a complete Entry and doesn't require reading the map to see if recovery is required. */ if(pFlogPair->Flog[CurrentFlogIndex].OldMap == pFlogPair->Flog[CurrentFlogIndex].NewMap) { NVDIMM_DBG("Flog[%d] Entry complete(initial state)", FlogNum); ReturnCode = EFI_SUCCESS; goto Finish; } /* convert pre-map LBA into an offset into the map */ MapEntryOffset = pArena->MapOffset + sizeof(BTT_MAP_ENTRIES) * BttGetMapFromLba(pFlogPair->Flog[CurrentFlogIndex].Lba); /* read current map Entry */ CHECK_RESULT(ReadNamespaceBytes( pBtt->pNamespace, MapEntryOffset, &Entry, sizeof(BTT_MAP_ENTRIES) ), Finish); CurrentMapPos = BttGetPositionInMapFromLba(pFlogPair->Flog[CurrentFlogIndex].Lba); CurrentMap = Entry.MapEntryLba [CurrentMapPos]; if (MapEntryIsInitial(CurrentMap)) { Entry.MapEntryLba[CurrentMapPos] = pFlogPair->Flog[CurrentFlogIndex].Lba | BTT_MAP_ENTRY_NORMAL; CurrentMap = Entry.MapEntryLba[CurrentMapPos]; } if(pFlogPair->Flog[CurrentFlogIndex].NewMap != CurrentMap && pFlogPair->Flog[CurrentFlogIndex].OldMap == CurrentMap) { /* last update didn't complete */ NVDIMM_VERB("recover Flog[%d]: map[%d]: %d", FlogNum, pFlogPair->Flog[CurrentFlogIndex].Lba, pFlogPair->Flog[CurrentFlogIndex].NewMap); /* Recovery step is to complete the transaction by updating the map Entry. */ Entry.MapEntryLba [CurrentMapPos] = pFlogPair->Flog[CurrentFlogIndex].NewMap; EFI_STATUS WriteResult = WriteNamespaceBytes( pBtt->pNamespace, MapEntryOffset, &Entry, sizeof(BTT_MAP_ENTRIES) ); if(EFI_ERROR(WriteResult)) { return WriteResult; } } ReturnCode = EFI_SUCCESS; Finish: return ReturnCode; } /* * The flog entries are not checksummed. Instead, increasing sequence * numbers are used to atomically switch the active flog entry between * the first and second struct btt_flog in each slot. In order for this * to work, the sequence number must be updated only after all the other * fields in the flog are updated. So the writes to the flog are broken * into two writes, one for the first three fields (lba, old_map, new_map) * and, only after those fields are known to be written durably, the * second write for the seq field is done. * * * NOTE: Our code differs from the spec in keeping a copy of the flog * pair around instead of just the current flog. */ STATIC EFI_STATUS BttFlogUpdate( IN BTT *pBtt, IN ARENAS *pArena, IN UINT32 Lba, IN UINT32 OldMap, IN UINT32 NewMap ) { BTT_FLOG * pCurrentFlog = NULL; BTT_FLOG * pNextFlog = NULL; BTT_FLOG_PAIR * pFlogPair = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT64 NextFlogOffset = 0; NVDIMM_DBG("pBtt=%p pArena=%p ", pBtt, pArena); NVDIMM_DBG("LBA=%x OldMap=%d NewMap=%d", Lba, OldMap, NewMap); if(!pBtt || !pArena) { return EFI_INVALID_PARAMETER; } pFlogPair = &(pArena->pFlogs[FLOG_PAIR_0].FlogPair); pNextFlog = &(pFlogPair->Flog[pArena->pFlogs[FLOG_PAIR_0].Next]); if (FLOG_0 == pArena->pFlogs[FLOG_PAIR_0].Next) { pCurrentFlog = &(pFlogPair->Flog[FLOG_1]); } else if (FLOG_1 == pArena->pFlogs[FLOG_PAIR_0].Next) { pCurrentFlog = &(pFlogPair->Flog[FLOG_0]); } else { NVDIMM_ERR("ERROR: Invalid FLOG[0].Next index value:%d\n", pArena->pFlogs[0].Next); return EFI_BAD_BUFFER_SIZE; } // Update the pNextFlog of our internal flog pair. We currently differ from // the reference implementation in keeping the flog pair instead of just // the current flog. pNextFlog->Lba = Lba; pNextFlog->OldMap = OldMap; pNextFlog->NewMap = NewMap; pNextFlog->Seq = NSEQ(pCurrentFlog->Seq); // Write out the pNextFlog entry to the dimm NextFlogOffset = pArena->pFlogs[0].Entry + pArena->pFlogs[0].Next*sizeof(BTT_FLOG); // write out first two fields first CHECK_RESULT(WriteNamespaceBytes(pBtt->pNamespace, NextFlogOffset, pNextFlog, sizeof(UINT32) * 2), Finish); NextFlogOffset += sizeof(UINT32) * 2; // write out new_map and seq field to make it active CHECK_RESULT(WriteNamespaceBytes(pBtt->pNamespace, NextFlogOffset, &(pNextFlog->NewMap), sizeof(UINT32) * 2), Finish); // Flog Entry written successfully, update run-time state pArena->pFlogs[0].Next = 1 - pArena->pFlogs[0].Next; NVDIMM_VERB("update Flog[0]: Lba=%d old=%d%s%s new %d%s%s", Lba, OldMap & BTT_MAP_ENTRY_LBA_MASK,(OldMap & BTT_MAP_ENTRY_ERROR) ? " ERROR" : "", (OldMap & BTT_MAP_ENTRY_ZERO) ? " ZERO" : "", NewMap & BTT_MAP_ENTRY_LBA_MASK, (NewMap & BTT_MAP_ENTRY_ERROR) ? " ERROR" : "",(NewMap & BTT_MAP_ENTRY_ZERO) ? " ZERO" : ""); ReturnCode = EFI_SUCCESS; Finish: return ReturnCode; } STATIC EFI_STATUS BttReadFlogs( IN BTT *pBtt, IN OUT ARENAS *pArena ) { EFI_STATUS ReadFlogPairResult; UINT64 FlogOffset = 0; FLOG_RUNTIME *pFlogRuntime = NULL; UINT32 Index = 0; if(!pBtt || !pArena) { return EFI_INVALID_PARAMETER; } pArena->pFlogs = (FLOG_RUNTIME *)AllocatePool(pBtt->NFree * sizeof(FLOG_RUNTIME)); if(!pArena->pFlogs) { NVDIMM_VERB("Memory allocation for %d Flog Entries", pBtt->NFree); return EFI_OUT_OF_RESOURCES; } ZeroMem(pArena->pFlogs, pBtt->NFree * sizeof(FLOG_RUNTIME)); /* Load up the Flog state. BttReadFlogPair() will determine if any recovery steps are required take them on the in-memory data structures it creates. Sets error flag when it determines an invalid state. */ FlogOffset = pArena->FlogOffset; pFlogRuntime = pArena->pFlogs; for(Index = 0; Index < pBtt->NFree; Index++) { ReadFlogPairResult = BttReadFlogPair(pBtt, pArena, FlogOffset, pFlogRuntime, Index); if(EFI_ERROR(ReadFlogPairResult)) { BttSetArenaError(pBtt, pArena); return ReadFlogPairResult; } /* prepare for Next time around the loop */ FlogOffset += ROUNDUP(sizeof(BTT_FLOG_PAIR), BTT_FLOG_PAIR_ALIGN); pFlogRuntime++; } return EFI_SUCCESS; } STATIC EFI_STATUS BttBuildRtt( IN BTT *pBtt, IN OUT ARENAS *pArena ) { UINT32 Lane = 0; if(!pBtt || !pArena) { return EFI_INVALID_PARAMETER; } pArena->pRtt = AllocatePool(pBtt->NFree * sizeof(UINT32)); if(!pArena->pRtt) { NVDIMM_DBG("Memory allocation for %d Rtt Entries failed", pBtt->NFree); return EFI_OUT_OF_RESOURCES; } for(Lane = 0; Lane < pBtt->NFree; Lane++) { pArena->pRtt[Lane] = BTT_MAP_ENTRY_ERROR; } return EFI_SUCCESS; } STATIC EFI_STATUS BttReadArena( IN BTT *pBtt, IN UINT64 ArenaOffset, IN OUT ARENAS *pArena ) { EFI_STATUS ReturnCode = EFI_SUCCESS; BTT_INFO *pBttInfo = NULL; NVDIMM_VERB("pBtt=%p pArena=%p ArenaOffset=%lld", pBtt, pArena, ArenaOffset); if (pBtt == NULL || pArena == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pBttInfo = (BTT_INFO *) AllocateZeroPool(sizeof(*pBttInfo)); if (pBttInfo == NULL) { NVDIMM_DBG("Memory allocation for BTT Info failed"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } NVDIMM_DBG("ArenaOffset=%lx", ArenaOffset); CHECK_RESULT(ReadNamespaceBytes(pBtt->pNamespace, ArenaOffset, pBttInfo, sizeof(BTT_INFO)), Finish); pArena->Flags = pBttInfo->Flags; pArena->ExternalNLbas = pBttInfo->ExternalNLbas; pArena->InternalLbaSize = pBttInfo->InternalLbaSize; pArena->InternalNLbas = pBttInfo->InternalNLbas; // pBttInfo offsets are relative to beginning of this arena's info block // pArena offsets are relative to beginning of encapsulating namespace pArena->StartOffset = ArenaOffset; pArena->DataOffset = ArenaOffset + pBttInfo->DataOffset; pArena->MapOffset = ArenaOffset + pBttInfo->MapOffset; pArena->FlogOffset = ArenaOffset + pBttInfo->FlogOffset; pArena->NextOffset = ArenaOffset + pBttInfo->NextOffset; CHECK_RESULT(BttReadFlogs(pBtt, pArena), Finish); CHECK_RESULT(BttBuildRtt(pBtt, pArena), Finish); Finish: FREE_POOL_SAFE(pBttInfo); return ReturnCode; } STATIC EFI_STATUS BttReadArenas( IN BTT *pBtt, IN UINT32 NArenas ) { UINT32 ArenasSize = 0; UINT64 ArenaOffset = 0; ARENAS *pArena = NULL; UINT32 Index = 0; if(!pBtt) { return EFI_INVALID_PARAMETER; } EFI_STATUS ErrorValue = EFI_INVALID_PARAMETER; ArenasSize = NArenas * sizeof(ARENAS); pBtt->Arenas = AllocatePool(ArenasSize); if(!pBtt->Arenas) { NVDIMM_DBG("Memory allocation for %d Arenas failed", NArenas); ErrorValue = EFI_OUT_OF_RESOURCES; goto RetVal; } ZeroMem(pBtt->Arenas, ArenasSize); /* Set ArenaOffset to PrimaryInfoOffset */ ArenaOffset = pBtt->PrimaryInfoOffset; pArena = pBtt->Arenas; EFI_STATUS ReadArenaResult; for(Index = 0; Index < NArenas; Index++) { ReadArenaResult = BttReadArena(pBtt, ArenaOffset, pArena); if(EFI_ERROR(ReadArenaResult)) { ErrorValue = ReadArenaResult; goto RetVal; } /* prepare for Next time around the loop */ ArenaOffset = pArena->NextOffset; pArena++; } pBtt->Laidout = TRUE; return EFI_SUCCESS; RetVal: NVDIMM_DBG("Error clean up"); if(pBtt->Arenas) { for(Index = 0; Index < pBtt->NArenas; Index++) { if(pBtt->Arenas[Index].pFlogs != NULL) { FreePool(pBtt->Arenas[Index].pFlogs); pBtt->Arenas[Index].pFlogs = NULL; } if(pBtt->Arenas[Index].pRtt != NULL) { FreePool((void *)pBtt->Arenas[Index].pRtt); pBtt->Arenas[Index].pRtt = NULL; } } FreePool(pBtt->Arenas); pBtt->Arenas = NULL; } return ErrorValue; } EFI_STATUS BttWriteLayout( IN BTT *pBtt, IN BOOLEAN Write ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT64 FlogSize = 0; UINT32 InternalLbaSize = 0; UINT64 TotalNLbas = 0; UINT64 RawSize = 0; UINT8 ArenaNumber = 0; UINT64 ArenaOffset = 0; UINT64 ArenaRawSize = 0; UINT64 ArenaDataSize = 0; UINT32 InternalNLbas = 0; UINT32 ExternalNLbas = 0; UINT64 MapSize = 0; UINT64 NextOffset = 0; UINT64 InfoOffset = 0; UINT64 FlogOffset = 0; UINT64 MapOffset = 0; UINT64 DataOffset = 0; UINT64 MapEntryOffset = 0; BTT_MAP_ENTRIES *pMap = NULL; UINT32 MapBlock = 0; UINT32 Index = 0; BTT_INFO *pBttInfo = NULL; UINT64 FlogEntryOffset = 0; UINT32 NextFreeLba = 0; BTT_FLOG_PAIR FlogPair; SetMem(&FlogPair, sizeof(FlogPair), 0x0); NVDIMM_VERB("pBtt=%p Write=%d", pBtt, Write); if (pBtt == NULL) { goto Finish; } if (pBtt->RawSize < BTT_NAMESPACE_MIN_SIZE) { goto Finish; } if (pBtt->NFree == 0) { goto Finish; } if (Write) { GenerateRandomGuid(&pBtt->Uuid); } /** The number of Arenas is the number of full arena of size BTT_MAX_ARENA that fit into RawSize and then, if the remainder is at least BTT_MIN_SIZE in size, then that adds one more arena. **/ pBtt->NArenas = (UINT8)(pBtt->RawSize / BTT_MAX_ARENA_SIZE); if(pBtt->RawSize % BTT_MAX_ARENA_SIZE >= BTT_NAMESPACE_MIN_SIZE) { pBtt->NArenas++; } NVDIMM_DBG("NArenas=%d", pBtt->NArenas); FlogSize = pBtt->NFree * ROUNDUP(sizeof(BTT_FLOG_PAIR), BTT_FLOG_PAIR_ALIGN); FlogSize = ROUNDUP(FlogSize, BTT_ALIGNMENT); InternalLbaSize = pBtt->LbaSize; if(InternalLbaSize < BTT_MIN_LBA_SIZE) { InternalLbaSize = BTT_MIN_LBA_SIZE; } InternalLbaSize = ROUNDUP(InternalLbaSize, CACHE_LINE_SIZE); /* check for overflow */ if(InternalLbaSize < CACHE_LINE_SIZE) { NVDIMM_DBG("Invalid LBA size after alignment: %d ", InternalLbaSize); goto Finish; } pBtt->InternalLbaSize = InternalLbaSize; NVDIMM_VERB("Adjusted InternalLbaSize: %d", InternalLbaSize); RawSize = pBtt->RawSize; pBttInfo = (BTT_INFO *) AllocateZeroPool(sizeof(*pBttInfo)); if (pBttInfo == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pMap = (BTT_MAP_ENTRIES *) AllocateZeroPool(BTT_ALIGNMENT); if (pMap == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } /** Set ArenaOffset to offset of 1st info block**/ ArenaOffset = pBtt->PrimaryInfoOffset; // for each arena while (RawSize >= BTT_NAMESPACE_MIN_SIZE) { NVDIMM_DBG("Layout arena %d", ArenaNumber); ArenaRawSize = RawSize; if(ArenaRawSize > BTT_MAX_ARENA_SIZE) { ArenaRawSize = BTT_MAX_ARENA_SIZE; } RawSize -= ArenaRawSize; ArenaNumber++; ArenaDataSize = ArenaRawSize; ArenaDataSize -= 2 * sizeof(BTT_INFO); ArenaDataSize -= FlogSize; /* allow for map alignment padding */ InternalNLbas = (UINT32)((ArenaDataSize - BTT_ALIGNMENT) / (InternalLbaSize + BTT_MAP_ENTRY_SIZE)); /* ensure the number of blocks is at least 2*NFree */ if (InternalNLbas < 2 * pBtt->NFree) { NVDIMM_DBG("Number of internal blocks: %x, expected at least %d", InternalNLbas, 2 * pBtt->NFree); goto Finish; } ExternalNLbas = InternalNLbas - pBtt->NFree; NVDIMM_DBG("InternalNLbas=%d ExternalNLbas=%d", InternalNLbas, ExternalNLbas); TotalNLbas += ExternalNLbas; MapSize = ROUNDUP(ExternalNLbas * BTT_MAP_ENTRY_SIZE, BTT_ALIGNMENT); ArenaDataSize -= MapSize; if (ArenaDataSize / InternalLbaSize < InternalNLbas) { ReturnCode = EFI_ABORTED; goto Finish; } /** The rest of the loop body calculates metadata structures and lays it out for this arena. So only continue if the write flag is set. **/ if (!Write) { continue; } InfoOffset = ArenaRawSize - sizeof(BTT_INFO); FlogOffset = InfoOffset - FlogSize; MapOffset = FlogOffset - MapSize; DataOffset = MapOffset - ArenaDataSize; if(RawSize >= BTT_NAMESPACE_MIN_SIZE) { NextOffset = BTT_MAX_ARENA_SIZE; } else { NextOffset = 0; } NVDIMM_DBG("Namespace offsets:"); NVDIMM_DBG("ArenaOffset 0x%012lx", ArenaOffset); NVDIMM_DBG("DataOffset 0x%012lx", ArenaOffset + DataOffset); NVDIMM_DBG("MapOffset 0x%012lx", ArenaOffset + MapOffset); NVDIMM_DBG("FlogOffset 0x%012lx", ArenaOffset + FlogOffset); NVDIMM_DBG("InfoOffset 0x%012lx", ArenaOffset + InfoOffset); NVDIMM_DBG("NextOffset 0x%012lx", ArenaOffset + NextOffset); /* Zero out the initial map, identity style */ MapEntryOffset = ArenaOffset + MapOffset; // Write map layout in 4k blocks for(MapBlock = 0; MapBlock <= MapSize / BTT_ALIGNMENT; MapBlock++) { ReturnCode = WriteNamespaceBytes (pBtt->pNamespace, MapEntryOffset + (MapBlock * BTT_ALIGNMENT), pMap, BTT_ALIGNMENT); if (EFI_ERROR(ReturnCode)) { goto Finish; } } /* write out the initial Flog */ FlogEntryOffset = ArenaOffset + FlogOffset; NextFreeLba = ExternalNLbas; ZeroMem(&FlogPair.Flog[1], sizeof(BTT_FLOG)); for (Index = 0; Index < pBtt->NFree; Index++) { FlogPair.Flog[0].Lba = 0; FlogPair.Flog[0].OldMap = FlogPair.Flog[0].NewMap = NextFreeLba; FlogPair.Flog[0].Seq = 1; /* Write both btt_Flog structs in the pair, writing the second one as all zeros. */ NVDIMM_VERB("Flog[%d] Entry off=%x initial %d + zero = %d", Index, FlogEntryOffset, NextFreeLba, NextFreeLba); ReturnCode = WriteNamespaceBytes (pBtt->pNamespace, FlogEntryOffset, &FlogPair, sizeof(BTT_FLOG_PAIR)); if (EFI_ERROR(ReturnCode)) { goto Finish; } FlogEntryOffset += sizeof(BTT_FLOG_PAIR); FlogEntryOffset = ROUNDUP(FlogEntryOffset, BTT_FLOG_PAIR_ALIGN); NextFreeLba++; } // Construct the BTT Info block and write it out at both the beginning and end of the arena. ZeroMem(pBttInfo, sizeof(*pBttInfo)); CopyMem_S(pBttInfo->Sig, sizeof(pBttInfo->Sig), Sig, BTTINFO_SIG_LEN); CopyMem_S(&pBttInfo->Uuid, sizeof(pBttInfo->Uuid), &pBtt->Uuid, sizeof(GUID)); CopyMem_S(&pBttInfo->ParentUuid, sizeof(pBttInfo->ParentUuid), &pBtt->ParentUuid, sizeof(GUID)); // Check BTT version. 2.0 offset is 0, 1.1 offset is 4K. if (pBtt->PrimaryInfoOffset == BTT_PRIMARY_INFO_BLOCK_OFFSET) { pBttInfo->Major = 2; pBttInfo->Minor = 0; } else { pBttInfo->Major = 1; pBttInfo->Minor = 1; } pBttInfo->ExternalLbaSize = pBtt->LbaSize; pBttInfo->ExternalNLbas = ExternalNLbas; pBttInfo->InternalLbaSize = InternalLbaSize; pBttInfo->InternalNLbas = InternalNLbas; pBttInfo->NFree = pBtt->NFree; pBttInfo->InfoSize = sizeof(*pBttInfo); // Following offsets are relative to the beginning of this arena info block pBttInfo->NextOffset = NextOffset; pBttInfo->DataOffset = DataOffset; pBttInfo->MapOffset = MapOffset; pBttInfo->FlogOffset = FlogOffset; pBttInfo->InfoOffset = InfoOffset; NVDIMM_DBG("BTT info block offsets:"); NVDIMM_DBG("DataOffset 0x%012lx", pBttInfo->DataOffset); NVDIMM_DBG("MapOffset 0x%012lx", pBttInfo->MapOffset); NVDIMM_DBG("FlogOffset 0x%012lx", pBttInfo->FlogOffset); NVDIMM_DBG("Info2Offset 0x%012lx", pBttInfo->InfoOffset); NVDIMM_DBG("NextOffset 0x%012lx", pBttInfo->NextOffset); ChecksumOperations((VOID *)pBttInfo, sizeof(BTT_INFO), &pBttInfo->Checksum, TRUE); ReturnCode = WriteNamespaceBytes(pBtt->pNamespace, ArenaOffset, pBttInfo, sizeof(BTT_INFO)); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = WriteNamespaceBytes(pBtt->pNamespace, ArenaOffset + InfoOffset, pBttInfo, sizeof(BTT_INFO)); if (EFI_ERROR(ReturnCode)) { goto Finish; } ArenaOffset += NextOffset; } if (pBtt->NArenas != ArenaNumber) { ReturnCode = EFI_ABORTED; goto Finish; } pBtt->NLbas = TotalNLbas; if (Write) { //The layout is written now, so load up the Arenas, and set laidout flag. BttReadArenas(pBtt, pBtt->NArenas); } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pMap); FREE_POOL_SAFE(pBttInfo); return ReturnCode; } STATIC EFI_STATUS BttReadLayout( IN BTT *pBtt ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 NArenas = 0; UINT32 SmallestNFree = MAX_UINT32_VALUE; UINT64 RawSize = 0; UINT64 TotalNLbas = 0; UINT64 ArenaOffset = 0; BTT_INFO *pBttInfo = NULL; NVDIMM_DBG("pBtt=%p", pBtt); if (pBtt == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pBttInfo = (BTT_INFO *) AllocateZeroPool(sizeof(*pBttInfo)); if (pBttInfo == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pBtt->NFree = BTT_DEFAULT_NFREE; RawSize = pBtt->RawSize; ArenaOffset = pBtt->PrimaryInfoOffset; // For each arena, see if there's a valid Info block while(RawSize >= BTT_NAMESPACE_MIN_SIZE) { NArenas++; NVDIMM_DBG("ArenaOffset: %llx", ArenaOffset); ReturnCode = ReadNamespaceBytes (pBtt->pNamespace, ArenaOffset, pBttInfo, sizeof(BTT_INFO)); if(EFI_ERROR(ReturnCode)) { goto Finish; } NVDIMM_DBG("BTT info block offsets:"); NVDIMM_DBG("DataOffset 0x%012lx", pBttInfo->DataOffset); NVDIMM_DBG("MapOffset 0x%012lx", pBttInfo->MapOffset); NVDIMM_DBG("FlogOffset 0x%012lx", pBttInfo->FlogOffset); NVDIMM_DBG("Info2Offset 0x%012lx", pBttInfo->InfoOffset); NVDIMM_DBG("NextOffset 0x%012lx", pBttInfo->NextOffset); ReturnCode = BttReadInfo(pBttInfo, pBtt); if (EFI_ERROR(ReturnCode)) { /** Failed to find complete BTT metadata. Just calculate the NArenas and NLbas values that will result when BttWriteLayout() gets called. This allows checks against NLbas to work correctly even before the layout is written. Need to check for a backup info block. If valid backup info block found, copy to primary info block. See UEFI 2.7 6.3.5 **/ ReturnCode = BttWriteLayout(pBtt, FALSE); goto Finish; } if(pBttInfo->ExternalLbaSize != pBtt->LbaSize) { /* can't read it assuming the wrong block size */ NVDIMM_DBG("inconsistent LbaSize, ns: %d btt:%d", pBttInfo->ExternalLbaSize, pBtt->LbaSize); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if(pBttInfo->NFree == 0) { NVDIMM_DBG("invalid NFree"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if(pBttInfo->ExternalNLbas == 0) { NVDIMM_DBG("invalid ExternalNLbas"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if(pBttInfo->NextOffset &&(pBttInfo->NextOffset != BTT_MAX_ARENA_SIZE)) { NVDIMM_DBG("invalid arena size: %llx", pBttInfo->NextOffset); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if(pBttInfo->NFree < SmallestNFree) { SmallestNFree = pBttInfo->NFree; } TotalNLbas += pBttInfo->ExternalNLbas; ArenaOffset += pBttInfo->NextOffset; if(pBttInfo->NextOffset == 0) { break; } if(pBttInfo->NextOffset > RawSize) { NVDIMM_DBG("invalid next arena offset. Next: %llx RawSize: %llx", pBttInfo->NextOffset, RawSize); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } RawSize -= pBttInfo->NextOffset; } if(!NArenas) { ReturnCode = EFI_ABORTED; goto Finish; } pBtt->NArenas = NArenas; pBtt->NLbas = TotalNLbas; // All Arenas were valid. NFree should be the smallest value found among different arenas. if(SmallestNFree < pBtt->NFree) { pBtt->NFree = SmallestNFree; } // Load up Arenas. ReturnCode = BttReadArenas(pBtt, NArenas); if (EFI_ERROR(ReturnCode)) { goto Finish; } Finish: FREE_POOL_SAFE(pBttInfo); return ReturnCode; } STATIC EFI_STATUS BttZeroBlock( IN BTT *pBtt, OUT VOID *pBuffer ) { if(!pBtt) { return EFI_INVALID_PARAMETER; } ZeroMem(pBuffer, pBtt->LbaSize); return EFI_SUCCESS; } STATIC EFI_STATUS BttLbaToArenaLba( IN BTT *pBtt, IN UINT64 Lba, OUT ARENAS **ppArena, OUT UINT32 *PreMapLba ) { UINT8 Arena = 0; if(!pBtt) { return EFI_INVALID_PARAMETER; } if(!pBtt->Laidout) { return EFI_ABORTED; } for(Arena = 0; Arena < pBtt->NArenas; Arena++) { if(Lba < pBtt->Arenas [Arena].ExternalNLbas) { break; } else { Lba -= pBtt->Arenas [Arena].ExternalNLbas; } } if(Arena >= pBtt->NArenas) { return EFI_ABORTED; } *ppArena = &pBtt->Arenas [Arena]; *PreMapLba = (UINT32)Lba; NVDIMM_VERB("pArena=%p PreMapLBA=%x", *ppArena, *PreMapLba); return EFI_SUCCESS; } /** Read a block from a btt namespace @param [in] pBtt namespace handle @param [in] Lba Logical block address to be read @param [out] pBuffer Read result Buffer pointer @retval EFI_SUCCESS if the routine succeeds **/ EFI_STATUS BttRead( IN BTT *pBtt, IN UINT64 Lba, OUT VOID *pBuffer ) { ARENAS *pArena = NULL; UINT32 PreMapLba = 0; UINT64 MapEntryOffset = 0; BTT_MAP_ENTRIES Entry; UINT8 PosInPreMapLba = 0; UINT32 CurrentMap = 0; INT8 MapCheck = 0; BTT_MAP_ENTRIES LatestEntry; UINT32 LatestMap = 0; UINT64 DataBlockOffset = 0; UINT32 LbaOut = 0; EFI_STATUS RetVal = EFI_SUCCESS; SetMem(&Entry, sizeof(Entry), 0x0); SetMem(&LatestEntry, sizeof(LatestEntry), 0x0); NVDIMM_VERB("pBtt=%p LBA=%x reading!", pBtt, Lba); if(!pBtt || !pBuffer) { return EFI_INVALID_PARAMETER; } EFI_STATUS Result = IsLbaValid(pBtt, Lba); if(EFI_ERROR(Result)) { return Result; } /* if there's no layout written yet, all reads come back as zeros */ if(!pBtt->Laidout) { return BttZeroBlock(pBtt, pBuffer); } /* find which arena LBA lives in, and the offset to the map Entry */ Result = BttLbaToArenaLba(pBtt, Lba, &pArena, &PreMapLba); if(EFI_ERROR(Result)) return Result; /* convert pre-map LBA into an offset into the map */ MapEntryOffset = pArena->MapOffset + sizeof(BTT_MAP_ENTRIES) * BttGetMapFromLba(PreMapLba); /* Read the current map Entry to get the post-map LBA for the data block read. */ Result = ReadNamespaceBytes (pBtt->pNamespace, MapEntryOffset, &Entry, sizeof(BTT_MAP_ENTRIES)); if(EFI_ERROR(Result)) { return Result; } PosInPreMapLba = BttGetPositionInMapFromLba(PreMapLba); CurrentMap = Entry.MapEntryLba[PosInPreMapLba]; /* Retries come back to the top of this loop(for a rare case where the map is changed by another thread doing writes to the same LBA). */ while(MapCheck == 0) { if(MapEntryIsError(CurrentMap)) { NVDIMM_DBG("EIO due to map Entry Error flag"); return EFI_ABORTED; } if(MapEntryIsZero(CurrentMap)) { return BttZeroBlock(pBtt, pBuffer); } /* Record the post-map LBA in the read tracking table during the read. The write will check Entries in the read tracking table before allocating a block for a write, waiting for outstanding reads on that block to complete. No need to mask off ERROR and ZERO bits since the above checks make sure they are clear at this point. */ pArena->pRtt[0] = CurrentMap; /* In case this thread was preempted between reading Entry and storing it in the Rtt, check to see if the map changed. If it changed, the block about to be read is at least free now (in the Flog, but that's okay since the data will still be undisturbed) and potentially allocated and being used for another write(data disturbed, so not okay to continue). */ Result = ReadNamespaceBytes (pBtt->pNamespace, MapEntryOffset, &LatestEntry, sizeof(BTT_MAP_ENTRIES)); if(EFI_ERROR(Result)) { pArena->pRtt[0] = BTT_MAP_ENTRY_ERROR; return Result; } LatestMap = LatestEntry.MapEntryLba [PosInPreMapLba]; if(CurrentMap == LatestMap) { MapCheck++; /* map stayed the same */ } else { CurrentMap = LatestMap; /* try again */ } } /* It is safe to read the block now, since the Rtt protects the block from getting re-allocated to something else by a write. Convert the offset in bytes to block offset */ // If map entry is in the initial state (post map lba should be zero as well), // use the pre map lba if (MapEntryIsInitial(CurrentMap)) { // Ignore whatever is in current map variable and set to pre map lba LbaOut = PreMapLba; } else { LbaOut = CurrentMap & BTT_MAP_ENTRY_LBA_MASK; } DataBlockOffset = pArena->DataOffset + (UINT64)(LbaOut) * pArena->InternalLbaSize; NVDIMM_DBG("LBA=%x->LBABtt=%x, Offset[B]=%lx", Lba, (UINT64) LbaOut, DataBlockOffset); RetVal = ReadNamespaceBytes (pBtt->pNamespace, DataBlockOffset, pBuffer, pBtt->LbaSize); /* done with read, so clear out Rtt Entry */ pArena->pRtt[0] = BTT_MAP_ENTRY_ERROR; return RetVal; } EFI_STATUS BttCheck( IN BTT *pBtt ) { EFI_STATUS retVal = EFI_SUCCESS; ARENAS *pArena = NULL; UINT8 Index = 0; NVDIMM_DBG("Btt %p", pBtt); if(!pBtt) { return EFI_INVALID_PARAMETER; } if(!pBtt->Laidout) { /* consistent by definition */ NVDIMM_DBG("no layout yet"); return retVal; } // for each arena pArena = pBtt->Arenas; for(Index = 0; Index < pBtt->NArenas; Index++) { // Perform the consistency checks for the arena. retVal = BttCheckArena(pBtt, pArena); if(EFI_ERROR(retVal)) { return retVal; } } return retVal; } STATIC EFI_STATUS BttCheckArena( IN BTT *pBtt, IN ARENAS *pArena ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Bitmapsize = 0; UINT8 *pBitmap = NULL; UINT32 MapEntry = 0; UINT32 RemainingMaps = 0; UINT32 MapsCount = 0; UINT8 MapEndPosition = 0; BTT_MAP_ENTRIES *pMap = NULL; UINT64 MapSize = 0; UINT32 MapBlock = 0; UINT32 Index = 0; UINT8 Position = 0; UINT8 CurrentFlogIndex = 0; UINT32 Entry = 0; NVDIMM_DBG("pBtt %p pArena %p", pBtt, pArena); if (pBtt == NULL || pArena == NULL) { goto Finish; } pMap = (BTT_MAP_ENTRIES *) AllocateZeroPool(BTT_ALIGNMENT); if (pMap == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } Bitmapsize = HOW_MANY(pArena->InternalNLbas, 8); pBitmap = (UINT8 *)AllocatePool(Bitmapsize); if(!pBitmap) { NVDIMM_DBG("!Memory allocation for Bitmap"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ZeroMem(pBitmap, Bitmapsize); /** Go through every post-map LBA mentioned in the map and make sure there are no duplicates. Bitmap is used to track which LBAs have been seen so far. **/ MapsCount = BttGetMapFromLba(pArena->ExternalNLbas); MapEndPosition = BttGetPositionInMapFromLba(pArena->ExternalNLbas); MapSize = ROUNDUP(pArena->ExternalNLbas * BTT_MAP_ENTRY_SIZE, BTT_ALIGNMENT); // Read entire map layout in 4k blocks for(MapBlock = 0; MapBlock <= MapSize / BTT_ALIGNMENT; MapBlock++) { ReturnCode = ReadNamespaceBytes (pBtt->pNamespace, pArena->MapOffset +(MapBlock * BTT_ALIGNMENT), pMap, BTT_ALIGNMENT); if(EFI_ERROR(ReturnCode)) { goto Finish; } if(MapsCount > MapBlock * BTT_ALIGNMENT / sizeof(BTT_MAP_ENTRIES)) { //protect overturn RemainingMaps = MapsCount - MapBlock * BTT_ALIGNMENT / sizeof(BTT_MAP_ENTRIES); } else { break; } for (Index = 0; Index < BTT_ALIGNMENT / sizeof(BTT_MAP_ENTRIES); Index++) { if(Index > RemainingMaps) { //last BTT_MAP_ENTRIES within this MapBlock break; } for (Position = 0; Position < BTT_MAP_LOCK_ALIGN / BTT_MAP_ENTRY_SIZE; Position++) { //End the loop for the last Lba if(Index == RemainingMaps && Position == MapEndPosition) { break; } MapEntry = pMap [Index].MapEntryLba [Position]; /* for debug, dump zero map Entries */ if((MapEntry & BTT_MAP_ENTRY_ZERO) == 0) { NVDIMM_VERB("map[%d]: %d%s%s", Index, MapEntry & BTT_MAP_ENTRY_LBA_MASK, (MapEntry & BTT_MAP_ENTRY_ERROR) ? " ERROR" : "",(MapEntry & BTT_MAP_ENTRY_ZERO) ? " ZERO" : ""); } if (MapEntryIsInitial(MapEntry)) { MapEntry = MapBlock * (BTT_ALIGNMENT / BTT_MAP_ENTRY_SIZE) + Index * (sizeof(BTT_MAP_ENTRIES) / BTT_MAP_ENTRY_SIZE) + Position; } else { MapEntry &= BTT_MAP_ENTRY_LBA_MASK; } /* check if entry is valid */ if(MapEntry >= pArena->ExternalNLbas) { NVDIMM_DBG("map[%d] Entry out of bounds: %d", Index, MapEntry); goto Finish; } if (IS_BIT_SET(pBitmap, MapEntry)) { NVDIMM_DBG("map[%d] duplicate Entry: %d", Index, MapEntry); ReturnCode = EFI_ABORTED; goto Finish; } else { SET_BIT(pBitmap, MapEntry); } } } } /* Go through the free blocks in the Flog, adding them to Bitmap and checking for duplications. It is sufficient to read the run-time Flog here, avoiding more calls to NsRead. */ for (Index = 0; Index < pBtt->NFree; Index++) { CurrentFlogIndex = 1 - pArena->pFlogs[Index].Next; Entry = pArena->pFlogs[Index].FlogPair.Flog[CurrentFlogIndex].OldMap; Entry &= BTT_MAP_ENTRY_LBA_MASK; if (IS_BIT_SET(pBitmap, Entry)) { NVDIMM_DBG("Flog[%d] duplicate Entry: %d", Index, Entry); ReturnCode = EFI_ABORTED; goto Finish; } else { SET_BIT(pBitmap, Entry); } } /* Make sure every possible post-map LBA was accounted for in the two loops above. */ for(Index = 0; Index < pArena->InternalNLbas; Index++) { if (IS_BIT_CLEARED(pBitmap, Index)) { NVDIMM_DBG("Unreferenced LBA: %d", Index); ReturnCode = EFI_ABORTED; goto Finish; } } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pBitmap); FREE_POOL_SAFE(pMap); return ReturnCode; } STATIC EFI_STATUS BttMapLock( IN BTT *pBtt, IN ARENAS *pArena, OUT BTT_MAP_ENTRIES *Entry, IN UINT32 PreMapLba ) { UINT32 MapNumber = 0; UINT32 MapPosition = 0; UINT64 MapEntryOffset = 0; UINT32 BttMapLockNum = 0; if(!pBtt || !pArena) { return EFI_INVALID_PARAMETER; } MapNumber = BttGetMapFromLba(PreMapLba); MapPosition = BttGetPositionInMapFromLba(PreMapLba); MapEntryOffset = pArena->MapOffset + sizeof(BTT_MAP_ENTRIES) * MapNumber; /* BttMapLock[] contains NFree locks which are used to protect the map from concurrent access to the same cache line. The index into BttMapLock[] is calculated by looking at the byte offset into the map (PreMapLba * BTT_MAP_ENTRY_SIZE), figuring out how many cache lines that is into the map that is(dividing by BTT_MAP_LOCK_ALIGN), and then selecting one of nfree locks(the modulo at the end). */ BttMapLockNum = MapNumber % pBtt->NFree; /* read the old map Entry */ EFI_STATUS ReadResult = ReadNamespaceBytes (pBtt->pNamespace, MapEntryOffset, Entry, sizeof(BTT_MAP_ENTRIES)); if(EFI_ERROR(ReadResult)) { return ReadResult; } /* if map entry is in its initial state return pre_map_lba */ if (MapEntryIsInitial(Entry->MapEntryLba[MapPosition])) { Entry->MapEntryLba[MapPosition] = PreMapLba | BTT_MAP_ENTRY_NORMAL; } NVDIMM_VERB("locked maps[%u], LBAs: %u - %u", BttMapLockNum, Entry->MapEntryLba[0] & BTT_MAP_ENTRY_LBA_MASK, Entry->MapEntryLba[CACHE_LINE_SIZE / sizeof(UINT32) - 1] & BTT_MAP_ENTRY_LBA_MASK); return EFI_SUCCESS; } STATIC EFI_STATUS BttMapUnlock( IN BTT *pBtt, IN OUT ARENAS *pArena, IN BTT_MAP_ENTRIES *Entry, IN UINT32 PreMapLba ) { UINT32 MapNumber = 0; UINT64 MapEntryOffset = 0; UINT32 BttMapLockNum = 0; NVDIMM_VERB("pBtt %p pArena %p Entry %p PreMapLba %u", pBtt, pArena, Entry, PreMapLba); if(!pBtt || !pArena) { return EFI_INVALID_PARAMETER; } MapNumber = BttGetMapFromLba(PreMapLba); MapEntryOffset = pArena->MapOffset + sizeof(BTT_MAP_ENTRIES) * MapNumber; BttMapLockNum = MapNumber % pBtt->NFree; /* write the new map Entry */ EFI_STATUS RetVal = WriteNamespaceBytes (pBtt->pNamespace, MapEntryOffset, Entry, sizeof(BTT_MAP_ENTRIES)); //pArena->BttMapLock [BttMapLockNum].IndSpinlockRelease(pMapLockHandle); NVDIMM_DBG("unlocked maps[%u], LBAs: %u - %u", BttMapLockNum, Entry->MapEntryLba [0] & BTT_MAP_ENTRY_LBA_MASK, Entry->MapEntryLba [CACHE_LINE_SIZE / sizeof(UINT32) - 1] & BTT_MAP_ENTRY_LBA_MASK); return RetVal; } /** Writes a block to a btt namespace @param [in] pBtt namespace handle @param [in] Lba Logical block address to be written @param [in] pBuffer Buffer pointer to the block to be written @retval EFI_SUCCESS if the routine succeeds **/ EFI_STATUS BttWrite( IN BTT *pBtt, IN UINT64 Lba, IN VOID *pBuffer ) { ARENAS *pArena = NULL; UINT32 PreMapLba = 0; UINT8 CurrentFlogIndex = 0; UINT32 FreeMap = 0; UINT32 Index = 0; UINT64 DataBlockOffset = 0; BTT_MAP_ENTRIES MapEntry; UINT8 PosInEntry = 0; UINT32 OldMap = 0; NVDIMM_VERB("pBtt=%p LBA=%x writing!", pBtt, Lba); SetMem(&MapEntry, sizeof(MapEntry), 0x0); if(!pBtt) { return EFI_INVALID_PARAMETER; } EFI_STATUS RetVal = IsLbaValid(pBtt, Lba); if(EFI_ERROR(RetVal)) { return RetVal; } /* first write through here will initialize the metadata layout */ if(!pBtt->Laidout) { RetVal = BttWriteLayout(pBtt, TRUE); if(EFI_ERROR(RetVal)) { return RetVal; } } /* find which arena LBA lives in, and the offset to the map Entry */ RetVal = BttLbaToArenaLba(pBtt, Lba, &pArena, &PreMapLba); if(EFI_ERROR(RetVal)) { return RetVal; } /* if the arena is in an Error state, writing is not allowed */ if(pArena->Flags & BTTINFO_FLAG_ERROR_MASK) { NVDIMM_DBG("EIO due to BttInfo Error Flags 0x%x", pArena->Flags & BTTINFO_FLAG_ERROR_MASK); return EFI_ABORTED; } /* This routine was passed a unique "Lane" which is an index into the Flog. That means the free block held by Flog[Lane] is assigned to this thread and to no other threads(no additional locking required). So start by performing the write to the free block. It is only safe to write to a free block if it doesn't appear in the read tracking table, so scan that first and if found, wait for the thread reading from it to finish. */ CurrentFlogIndex = 1 - pArena->pFlogs[0].Next; FreeMap = (pArena->pFlogs[0].FlogPair.Flog[CurrentFlogIndex].OldMap & BTT_MAP_ENTRY_LBA_MASK) | BTT_MAP_ENTRY_NORMAL; NVDIMM_VERB("FreeMap=%x(before mask %x)", FreeMap, pArena->pFlogs[0].FlogPair.Flog[CurrentFlogIndex].OldMap); /* wait for other threads to finish any reads on free block */ for(Index = 0; Index < pBtt->NLanes; Index++) { while(pArena->pRtt[Index] == FreeMap) { ; } } // to be deleted in UEFI // it is now safe to perform write to the free block DataBlockOffset = pArena->DataOffset + (UINT64)(FreeMap & BTT_MAP_ENTRY_LBA_MASK) * pArena->InternalLbaSize; NVDIMM_DBG("LBA=%x->LBABtt=%x Offset[B]=%lx", Lba, (UINT64) FreeMap & BTT_MAP_ENTRY_LBA_MASK, DataBlockOffset); RetVal = WriteNamespaceBytes (pBtt->pNamespace, DataBlockOffset, pBuffer, pBtt->LbaSize); if(EFI_ERROR(RetVal)) { return RetVal; } // Make the new block active atomically by updating the on-media Flog and then updating the map. RetVal = BttMapLock(pBtt, pArena, &MapEntry, PreMapLba); if(EFI_ERROR(RetVal)) { return RetVal; } /* update the Flog */ PosInEntry = BttGetPositionInMapFromLba(PreMapLba); OldMap = MapEntry.MapEntryLba[PosInEntry]; RetVal = BttFlogUpdate(pBtt, pArena, PreMapLba, OldMap, FreeMap); if(EFI_ERROR(RetVal)) { NVDIMM_DBG("Could not update the BTT Flog!\npBtt %p pArena %p PreMapLba %u", pBtt, pArena, PreMapLba); return RetVal; } MapEntry.MapEntryLba [PosInEntry] = FreeMap; RetVal = BttMapUnlock(pBtt, pArena, &MapEntry, PreMapLba); if(EFI_ERROR(RetVal)) { BttSetArenaError(pBtt, pArena); return RetVal; } return EFI_SUCCESS; } EFI_STATUS BttArenaSetFlag( IN BTT *pBtt, IN OUT ARENAS *pArena, IN UINT32 SetFlag ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT64 ArenaOff = 0; BTT_INFO *pBttInfo = NULL; /* update runtime state */ pArena->Flags = SetFlag; if (!pBtt->Laidout) { /* no layout yet to update */ ReturnCode = EFI_ABORTED; goto Finish; } pBttInfo = (BTT_INFO *) AllocateZeroPool(sizeof(*pBttInfo)); if (pBttInfo == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } /* Read, modify and write out the info block at both the beginning and end of the arena. */ ArenaOff = pArena->StartOffset; /* protect from simultaneous writes to the layout */ ReturnCode = ReadNamespaceBytes (pBtt->pNamespace, ArenaOff, pBttInfo, sizeof(BTT_INFO)); if (EFI_ERROR(ReturnCode)) { goto Finish; } /* update flags */ pBttInfo->Flags |= SetFlag; /* update checksum */ ChecksumOperations((VOID *)pBttInfo, sizeof(BTT_INFO), &pBttInfo->Checksum, TRUE); ReturnCode = WriteNamespaceBytes (pBtt->pNamespace, ArenaOff, pBttInfo, sizeof(BTT_INFO)); if(EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = WriteNamespaceBytes (pBtt->pNamespace, ArenaOff + pBttInfo->InfoOffset, pBttInfo, sizeof(BTT_INFO)); if(EFI_ERROR(ReturnCode)) { goto Finish; } Finish: FREE_POOL_SAFE(pBttInfo); return ReturnCode; } EFI_STATUS BttSetArenaError( IN BTT *pBtt, IN OUT ARENAS *pArena ) { return BttArenaSetFlag(pBtt, pArena, BTTINFO_FLAG_ERROR); } VOID BttRelease( IN OUT BTT *pBtt ) { UINT8 Index = 0; NVDIMM_DBG("pBtt %p", pBtt); ASSERT(pBtt != NULL); if(pBtt) { if(pBtt->Arenas) { for(Index = 0; Index < pBtt->NArenas; Index++) { if(pBtt->Arenas[Index].pFlogs) { FreePool(pBtt->Arenas[Index].pFlogs); } if(pBtt->Arenas[Index].pRtt) { FreePool((UINT32 *)pBtt->Arenas[Index].pRtt); } //if(pBtt->Arenas[Index].BttMapLock) { // FreePool(pBtt->Arenas[Index].BttMapLock); //} } FreePool(pBtt->Arenas); } FreePool(pBtt); } } UINT32 BttGetMapFromLba( IN UINT32 Lba ) { return Lba / (BTT_MAP_LOCK_ALIGN / BTT_MAP_ENTRY_SIZE); } UINT8 BttGetPositionInMapFromLba( IN UINT32 Lba ) { return Lba % (BTT_MAP_LOCK_ALIGN / BTT_MAP_ENTRY_SIZE); } ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Btt.h000066400000000000000000000230771440615110200202360ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef __BTT_H__ #define __BTT_H__ #include "BttLayout.h" extern GUID gBttAbstractionGuid; /* Flog pair indices */ #define FLOG_0 0 //!< 0th Flog entry #define FLOG_1 1 //!< 1st Flog entry #define FLOG_PAIR_0 0 //!< 0th Flog pair #define EFI_BTT_ABSTRACTION_GUID \ { 0x18633BFC, 0x1735, 0x4217, {0x8A, 0xC9, 0x17, 0x23, 0x92, 0x82, 0xD3, 0xF8} } #define HOW_MANY(x, y) ((((x) % (y)) == 0) ? ((x) / (y)) : (((x) / (y)) + 1)) //!< Macro defining how many objects of the size x is required to store object of the size y #define SET_BIT(a,i) ((a)[(i)/8] |= 1<<((i)%8)) //!< Sets i'th bit in 'a' byte array #define CLR_BIT(a,i) ((a)[(i)/8] &= ~(1<<((i)%8))) //!< Clears i'th bit in 'a' byte array #define IS_BIT_SET(a,i) ((a)[(i)/8] & (1<<((i)%8))) //!< Checks if i'th bit in 'a' byte array is set #define IS_BIT_CLEARED(a,i) (((a)[(i)/8] & (1<<((i)%8))) == 0) //!< Checks if i'th bit in 'a' byte array is cleared /** Converts Lba into BTT_MAP_ENTRIES number @retval Number of BTT_MAP_ENTRIES structure within MAP region @param [in] Lba Block for which output is calculated **/ #define BTT_GET_MAP_FROM_LBA(Lba) (Lba / (BTT_MAP_LOCK_ALIGN / BTT_MAP_ENTRY_SIZE)) /** Converts Lba into position within BTT_MAP_ENTRIES structure @retval Position of BTT map within BTT_MAP_ENTRIES structure @param [in] Lba Block for which output is calculated **/ #define BTT_GET_POSITION_IN_MAP_FROM_LBA(Lba) (Lba % (BTT_MAP_LOCK_ALIGN / BTT_MAP_ENTRY_SIZE)) /** Structure for keeping Flog Entries in runtime **/ typedef struct _FLOG_RUNTIME { BTT_FLOG_PAIR FlogPair; //!< current Info UINT64 Entry; //!< offset for Flog pair UINT8 Next; //!< Next write (0 or 1) } FLOG_RUNTIME; //!< @see _FLOG_RUNTIME /** Structure for keeping Arena Informations **/ typedef struct _ARENAS { UINT32 Flags; //!< arena Flags (btt_Info) UINT32 ExternalNLbas; //!< Advertised number of LBAs in this arena. UINT32 InternalLbaSize; //!< Internal LBA size. Each block in the arena data area is this size in bytes. //!< This may be larger than the ExternalLbaSize due to alignment padding between LBAs. UINT32 InternalNLbas; //!< Number of blocks in the arena data area /** The following offsets are relative to the beginning of the encapsulating namespace. This is different from how these offsets are stored on-media, where they are relative to the start of the arena. The offset are converted by BttReadLayout() to make them more convenient for run-time use. **/ UINT64 StartOffset; //!< offset to start of arena UINT64 DataOffset; //!< offset to arena data area UINT64 MapOffset; //!< offset to area map UINT64 FlogOffset; //!< offset to area Flog UINT64 NextOffset; //!< offset to Next arena /** Run-time Flog state. The write path uses the Flog to find the free block it writes to before atomically making it the new active block for an external LBA. The read path doesn't use the Flog at all. **/ FLOG_RUNTIME *pFlogs; /** Read tracking table. Before using a free block found in the Flog, the write path scans the Rtt to see if there are any outstanding reads on that block (reads that started before the block was freed by a concurrent write). Unused slots in the Rtt are indicated by setting the error bit, BTT_MAP_ENTRY_ERROR, so that the entry won't match any post-map LBA when checked. **/ UINT32 volatile *pRtt; } ARENAS; //!< @see _ARENAS /** The opaque btt handle containing state tracked by this module for the btt namespace. This is created by BttInit(), handed to all the other btt_* entry points, and deleted by BttRelease(). **/ typedef struct _BTT { /** The Laidout flag indicates whether the namespace contains valid BTT metadata. It is initialized by BttReadLayout() and if no valid layout is found, all reads return zeros and the first write will write the BTT layout. **/ BOOLEAN Laidout; /** Number of concurrent threads allowed per btt **/ UINT32 NLanes; /** UUID of the BTT **/ GUID Uuid; /** UUID of the containing namespace, used to validate BTT metadata. **/ GUID ParentUuid; /** Parameters controlling/describing the BTT layout. **/ UINT64 RawSize; //!< Size of containing namespace UINT32 LbaSize; //!< External LBA size UINT32 InternalLbaSize; //!< Internal LBA size, physical block size for the Windows UINT32 NFree; //!< Available Flog Entries UINT64 NLbas; //!< Total number of external LBAs UINT32 NArenas; //!< Number of Arenas UINT64 PrimaryInfoOffset; //!< BTT Info Block offset on first arena /** Run-time state kept for each arena **/ ARENAS *Arenas; // This pointer is VOID instead of NAMESPACE to avoid includes looping VOID *pNamespace; // The pointer to the containing namespace for the IO operations } BTT; //!< @see _BTT /** Signature for arena Info blocks. Total size is 16 bytes, including the '\0' added to the string by the declaration (the last two bytes of the string are '\0'). **/ static const char Sig [] = "BTT_ARENA_INFO\0"; /** Lookup table of Sequence numbers. These are the 2-bit numbers that cycle between 01, 10, and 11. **/ static const unsigned NSeq [] = {0, 2, 3, 1}; #define NSEQ(Seq) (NSeq[(Seq) & 3]) //!< Macro for looking up Sequence numbers.\n //!< To advance a Sequence number to the Next number, use something like:\n //!< Seq = NSEQ (Seq); /** Validates btt info block @retval EFI_SUCCESS if the routine succeeds @param [in] pInfo Info block to be validated @param [in] pBtt BTT to be compared with existing metadata **/ EFI_STATUS BttReadInfo( IN BTT_INFO *pInfo, IN BTT *pBtt ); /** Prepare a btt namespace for use, returning an opaque handle When submitted a pristine namespace it will be formatted implicitly when touched for the first time. If arenas have different NFree values, we will be using the lowest one found as limiting to the overall "bandwidth". @retval PBtt namespace handle, NULL on error @param [in] RawSize Size of btt namespace being created @param [in] LbaSize Size of a block in a created namespace @param [in] ParentUuid[] UUID label of the namespace @param [in] pNamespace pointer to the BTTs parent namespace @param [in] pIsBttInitialized pointer to the flag if the BTT was initialized before. **/ BTT * BttInit( IN UINT64 RawSize, IN UINT32 LbaSize, IN GUID *pParentUuid, IN VOID *pNamespace ); /** Writes out the initial btt metadata layout Called with Write == TRUE only once in the life time of a btt namespace, when the first write happens. The caller of this routine is responsible for locking out multiple threads. This routine doesn't read anything -- by the time it is called, it is known there's no layout in the namespace and a new layout should be written. Calling with Write == FALSE tells this routine to do the calculations for Bttp->NArenas and Bttp->NLbas, but don't write out any metadata. If successful, sets Bttp->Laidout to 1. Otherwise Bttp->Laidout remains 0 so that later attempts to write will try again to create the layout. @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [in] Write Switch informing whether calculated metadata should be written **/ EFI_STATUS BttWriteLayout( IN BTT *pBtt, IN BOOLEAN Write ); /** Read a block from a btt namespace @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [in] Lba Logical block address to be read @param [out] pBuffer Read result Buffer pointer **/ EFI_STATUS BttRead ( IN BTT* pBtt, IN UINT64 Lba, OUT VOID* pBuffer ); /** Writes a block to a btt namespace @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [in] Lba Logical block address to be written @param [in] pBuffer Buffer pointer to the block to be written **/ EFI_STATUS BttWrite ( IN BTT* pBtt, IN UINT64 Lba, IN VOID* pBuffer ); /** Deletes opaque Btt_Info, done using btt namespace @param [in,out] pBtt namespace handle **/ VOID BttRelease ( IN OUT BTT* pBtt ); /** Marks a block as in an error state in a btt namespace @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt namespace handle @param [in] Lba Logical block address **/ EFI_STATUS BttSetError ( IN BTT* pBtt, IN UINT32 Lba ); /** Updates the given flag for the arena info block @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt Namespace handle @param [in] pArena Pointer to the Arena @param [in] SetFlag Flag to be set **/ EFI_STATUS BttArenaSetFlag( IN BTT *pBtt, IN OUT ARENAS *pArena, IN UINT32 SetFlag ); /** Set the error flag for the given arena @retval EFI_SUCCESS if the routine succeeds @param [in] pBtt Namespace handle @param [in] pArena Pointer to the Arena **/ EFI_STATUS BttSetArenaError( IN BTT *pBtt, IN OUT ARENAS *pArena ); UINT32 BttGetMapFromLba( IN UINT32 Lba ); UINT8 BttGetPositionInMapFromLba( IN UINT32 Lba ); #endif //__BTT_H__ ipmctl-03.00.00.0485/DcpmPkg/driver/Core/BttLayout.h000066400000000000000000000107541440615110200214320ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _BTT_LAYOUT_H_ #define _BTT_LAYOUT_H_ #define BTT_ALIGNMENT 4096 //!< Alignment of all BTT structures #define BTTINFO_SIG_LEN 16 //!< Signature length #define BTT_PRIMARY_INFO_BLOCK_OFFSET 0 //!< BTT Info Block offset on first arena #define BTT_PRIMARY_INFO_BLOCK_OFFSET_1_1 4096 //!< BTT Info Block offset on first arena for 1.1 #include #pragma pack(push, 1) /*! Layout of BTT Info block. */ typedef struct _BTT_INFO { UINT8 Sig [BTTINFO_SIG_LEN]; //!< must be "BTT_ARENA_INFO\0\0" GUID Uuid; //!< BTT UUID GUID ParentUuid; //!< UUID of container UINT32 Flags; //!< see flag bits below UINT16 Major; //!< major version UINT16 Minor; //!< minor version UINT32 ExternalLbaSize; //!< advertised LBA size (bytes) UINT32 ExternalNLbas; //!< advertised LBAs in this arena UINT32 InternalLbaSize; //!< size of data area blocks (bytes) UINT32 InternalNLbas; //!< number of blocks in data area UINT32 NFree; //!< number of free blocks UINT32 InfoSize; //!< size of this Info block /*! The following offsets are relative to the beginning of the BttInfo block. */ UINT64 NextOffset; //!< offset to next arena (or zero) UINT64 DataOffset; //!< offset to arena data area UINT64 MapOffset; //!< offset to area map UINT64 FlogOffset; //!< offset to area flog UINT64 InfoOffset; //!< offset to backup info block UINT8 Unused [3968]; //!< alignment to BTT_ALIGNMENT 4096 UINT64 Checksum; //!< Fletcher64 of all fields } BTT_INFO; //!< @see _BTT_INFO /*! Definitions for flags mask for BTT_INFO structure above. */ #define BTTINFO_FLAG_ERROR 0x00000001 //!< error state (read-only) #define BTTINFO_FLAG_ERROR_MASK 0x00000001 //!< all error bits /*! Layout of a BTT "Flog" entry. The "NoFree" field in the BTT Info block determines how many of these Flog Entries there are, and each entry consists of two of the following structs (entry updates alternate between the two structs), padded up to a cache line boundary to isolate adjacent updates. */ typedef struct _BTT_FLOG { UINT32 Lba; //!< last pre-map LBA using this entry UINT32 OldMap; //!< old post-map LBA (the freed block) UINT32 NewMap; //!< new post-map LBA UINT32 Seq; //!< Sequence number (01, 10, 11) } BTT_FLOG; //!< @see _BTT_FLOG /*! Padding to cache line boundary to isolate adjacent updates. */ #define BTT_FLOG_PAIR_ALIGN 64 /*! BTT Flog pair */ typedef struct _BTT_FLOG_PAIR { BTT_FLOG Flog [2]; //!< Flog Pair UINT8 unused [BTT_FLOG_PAIR_ALIGN - 2*sizeof (BTT_FLOG)]; //!< Padding to 64 bytes } BTT_FLOG_PAIR; //!< @see _BTT_FLOG_PAIR /*! Struct mapping between External and Internal LBA, padded to 64 bytes as that's the minimum chunk that can be saved via block apertures */ typedef struct _BTT_MAP_ENTRIES { UINT32 MapEntryLba [CACHE_LINE_SIZE / sizeof(UINT32)]; //!< Mapping between external to internal LBA } BTT_MAP_ENTRIES; /*! Layout of a BTT "map" entry. 4-byte internal LBA offset. */ #define BTT_MAP_ENTRY_INITIAL (0) //!< Both error and zero bits are 0 #define BTT_MAP_ENTRY_ZERO (1u << 31) //!< Zero'th bit #define BTT_MAP_ENTRY_ERROR (1u << 30) //!< Error bit #define BTT_MAP_ENTRY_NORMAL (3u << 30) //!< Normal map entry has both the zero and error bits set #define BTT_MAP_ENTRY_LBA_MASK 0x3fffffff //!< Lba mask #define BTT_MAP_ENTRY_SIZE 4 //!< Size of Map Entry #define BTT_MAP_LOCK_ALIGN 64 //!< Map alignment /*! BTT layout properties... */ #define BTT_NAMESPACE_MIN_SIZE MIB_TO_BYTES(16) //!< Btt namespace minimal size = 16MiB #define BTT_MAX_ARENA_SIZE GIB_TO_BYTES(512ULL) //!< Arena maximum size = 512GiB #define BTT_MIN_LBA_SIZE 512 //!< Minimal Lba size = 512 bytes #define BTT_INTERNAL_LBA_ALIGNMENT 256 //!< Lba byte alignment #define BTT_DEFAULT_NFREE 256 //!< Default number of flog entries #pragma pack(pop) #endif //_BTT_LAYOUT_H_ ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Diagnostics/000077500000000000000000000000001440615110200215725ustar00rootroot00000000000000ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Diagnostics/ConfigDiagnostic.c000066400000000000000000001575241440615110200251660ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include "ConfigDiagnostic.h" extern NVMDIMMDRIVER_DATA *gNvmDimmData; #define DIMMSPECS_TEST_INDEX 0 #define DUPLICATE_DIMM_TEST_INDEX 1 #define SYSTEMCAP_TEST_INDEX 2 #define NAMESPACE_LSA_TEST_INDEX 3 #define PCD_TEST_INDEX 4 /** Check if the Namespace Label area can be read and if it is in a format we understand @param[in] pDimm Pointer to the DIMM @param[in] DimmCount DIMMs count @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[in out] ppResult Pointer to the result string of platform config diagnostics message @param[out] pDiagState Pointer to the platform config diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL @retval EFI_NOT_FOUND Unable to locate relevant PCAT tables. **/ STATIC EFI_STATUS CheckNamespaceLabelAreaIndex( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT8 DimmIdPreference, IN OUT CHAR16 **ppResultStr, OUT UINT8 *pDiagState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; CHAR16 DimmUid[MAX_DIMM_UID_LENGTH]; LABEL_STORAGE_AREA *pLabelStorageArea = NULL; UINT16 Index = 0; NVDIMM_ENTRY(); if (DimmCount == 0 || ppDimms == NULL || DimmCount > MAX_DIMMS || ppResultStr == NULL || pDiagState == NULL) { ReturnCode = EFI_INVALID_PARAMETER; if (pDiagState != NULL) { *pDiagState |= DIAG_STATE_MASK_ABORTED; } goto Finish; } for (Index = 0; Index < DimmCount; ++Index) { ReturnCode = GetDimmUid(ppDimms[Index], DimmUid, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("GetDimmUid function for DIMM ID 0x%x failed.", ppDimms[Index]->DeviceHandle.AsUint32); continue; } ReturnCode = GetPreferredValueAsString(ppDimms[Index]->DeviceHandle.AsUint32, DimmUid, DimmIdPreference == DISPLAY_DIMM_ID_HANDLE, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("GetPreferredValueAsString function for DIMM ID 0x%x failed.", ppDimms[Index]->DeviceHandle.AsUint32); continue; } ReturnCode = ReadLabelStorageArea(ppDimms[Index]->DimmID, &pLabelStorageArea); if (EFI_ERROR(ReturnCode) && ReturnCode != EFI_NOT_FOUND) { APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_CONFIG_UNABLE_TO_READ_NS_INFO), EVENT_CODE_622, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState); } else { ReturnCode = EFI_SUCCESS; } FreeLsaSafe(&pLabelStorageArea); } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Parse and retrieve the sub table status fields from a COUT table. If a sub table is not discovered the MAX value for the data type will be returned since 0 is a valid status. @param[in] pConfigOutput, COUT table to parse @param[out] pPartitionSizeChangeTableStatus, Status from the partition size change table @param[out] pInterleaveInformationTableStatus_1, Status from the first discovered interleave information table @param[out] pInterleaveInformationTableStatus_2, Status from the second discovered interleave information table **/ STATIC VOID GetPlatformConfigOutputPCATStatus( IN NVDIMM_PLATFORM_CONFIG_OUTPUT *pConfigOutput, OUT UINT32 *pPartitionSizeChangeTableStatus, OUT UINT8 *pInterleaveInformationTableStatus_1, OUT UINT8 *pInterleaveInformationTableStatus_2 ) { NVDIMM_PARTITION_SIZE_CHANGE *pPartSizeChange = NULL; PCAT_TABLE_HEADER *pCurPcatTable = NULL; UINT32 SizeOfPcatTables = 0; UINT8 InterleaveTableNumber = 1; if (pConfigOutput == NULL || pPartitionSizeChangeTableStatus == NULL || pInterleaveInformationTableStatus_1 == NULL || pInterleaveInformationTableStatus_2 == NULL) { NVDIMM_DBG("Invalid Parameter"); return; } *pPartitionSizeChangeTableStatus = MAX_UINT32; *pInterleaveInformationTableStatus_1 = MAX_UINT8; *pInterleaveInformationTableStatus_2 = MAX_UINT8; /** Check if there is at least one PCAT table **/ if (pConfigOutput->Header.Length <= sizeof(*pConfigOutput)) { return; } pCurPcatTable = (PCAT_TABLE_HEADER *) &pConfigOutput->pPcatTables; SizeOfPcatTables = pConfigOutput->Header.Length - (UINT32) ((UINT8 *)pCurPcatTable - (UINT8 *)pConfigOutput); while ((UINT32) ((UINT8 *)pCurPcatTable - (UINT8 *)&pConfigOutput->pPcatTables) < SizeOfPcatTables) { if (pCurPcatTable->Type == PCAT_TYPE_PARTITION_SIZE_CHANGE_TABLE) { pPartSizeChange = (NVDIMM_PARTITION_SIZE_CHANGE *) pCurPcatTable; *pPartitionSizeChangeTableStatus = pPartSizeChange->PartitionSizeChangeStatus; pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pPartSizeChange->Header.Length); } else if (pCurPcatTable->Type == PCAT_TYPE_INTERLEAVE_INFORMATION_TABLE) { if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pConfigOutput)) { NVDIMM_INTERLEAVE_INFORMATION *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION *)pCurPcatTable; if (InterleaveTableNumber == 1) { *pInterleaveInformationTableStatus_1 = pInterleaveInfo->InterleaveChangeStatus; InterleaveTableNumber++; } else { *pInterleaveInformationTableStatus_2 = pInterleaveInfo->InterleaveChangeStatus; } pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pInterleaveInfo->Header.Length); } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pConfigOutput)) { NVDIMM_INTERLEAVE_INFORMATION3 *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION3 *)pCurPcatTable; if (InterleaveTableNumber == 1) { *pInterleaveInformationTableStatus_1 = pInterleaveInfo->InterleaveChangeStatus; InterleaveTableNumber++; } else { *pInterleaveInformationTableStatus_2 = pInterleaveInfo->InterleaveChangeStatus; } pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pInterleaveInfo->Header.Length); } } else { NVDIMM_DBG("Unknown table discovered in COUT"); break; } } } /** Create the detailed status string for when current configuration failed to apply @param[in] CCurStatus, Status byte from the CCUR table @retval NULL - failed to retrieve string from HII strings DB @retval Formatted CHAR16 string **/ STATIC CHAR16* GetCCurDetailedStatusStr( IN UINT16 CCurStatus ) { CHAR16 *pTmpStr = NULL; CHAR16 *pCCurErrorMessage = NULL; CHAR16 *pReturnStr = NULL; NVDIMM_ENTRY(); pTmpStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_DIAG_CCUR_CONFIG_DETAILED_STATUS), NULL); switch (CCurStatus) { case DIMM_CONFIG_CPU_MAX_MEMORY_LIMIT_VIOLATION: pCCurErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_CPU_MAX_MEMORY_LIMIT_VIOLATION), NULL); break; case DIMM_CONFIG_DCPMM_NM_FM_RATIO_UNSUPPORTED: pCCurErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_NM_FM_RATIO_UNSUPPORTED), NULL); break; case DIMM_CONFIG_DCPMM_POPULATION_ISSUE: pCCurErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_POPULATION_VIOLATION), NULL); break; case DIMM_CONFIG_PM_MAPPED_VM_POPULATION_ISSUE: pCCurErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_POPULATION_VIOLATION_BUT_PM_MAPPED), NULL); break; default: pCCurErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_UNKNOWN), NULL); break; } pReturnStr = CatSPrint(NULL, pTmpStr, CCurStatus, pCCurErrorMessage); FREE_POOL_SAFE(pTmpStr); FREE_POOL_SAFE(pCCurErrorMessage); NVDIMM_EXIT(); return pReturnStr; } /** Create the detailed status string for when a goal failed to apply Since zero is a valid status and N/A is a possible return value. The MAX value for a given status data type will be interpreted as N/A @param[in] CoutStatus, Status byte from the COUT table @param[in] PartitionSizeChangeTableStatus, Status from the partition size change table @param[in] InterleaveInformationTableStatus_1, Status from the first discovered interleave information table @param[in] InterleaveInformationTableStatus_2, Status from the second discovered interleave information table @retval NULL - failed to retrieve string from HII strings DB @retval Formatted CHAR16 string **/ STATIC CHAR16* GetCoutDetailedStatusStr( IN UINT8 CoutStatus, IN UINT32 PartitionSizeChangeTableStatus, IN UINT8 InterleaveInformationTableStatus_1, IN UINT8 InterleaveInformationTableStatus_2 ) { CHAR16 PartitionSizeChangeTableStatusStr[MAX_PCD_TABLE_STATUS_LENGTH]; CHAR16 InterleaveInformationTableStatus_1Str[MAX_PCD_TABLE_STATUS_LENGTH]; CHAR16 InterleaveInformationTableStatus_2Str[MAX_PCD_TABLE_STATUS_LENGTH]; CHAR16 *pTmpStr = NULL; CHAR16 *pCoutErrorMessage = NULL; CHAR16 *pTmpStr1 = NULL; CHAR16 *pReturnStr = NULL; ZeroMem(PartitionSizeChangeTableStatusStr, sizeof(PartitionSizeChangeTableStatusStr)); ZeroMem(InterleaveInformationTableStatus_1Str, sizeof(InterleaveInformationTableStatus_1Str)); ZeroMem(InterleaveInformationTableStatus_2Str, sizeof(InterleaveInformationTableStatus_2Str)); NVDIMM_ENTRY(); pTmpStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DIAGNOSTIC_NOT_AVAILABLE), NULL); if (pTmpStr != NULL) { if (PartitionSizeChangeTableStatus == MAX_UINT32) { StrnCpyS(PartitionSizeChangeTableStatusStr, MAX_PCD_TABLE_STATUS_LENGTH, pTmpStr, MAX_PCD_TABLE_STATUS_LENGTH - 1); } else { UnicodeSPrint(PartitionSizeChangeTableStatusStr, sizeof(PartitionSizeChangeTableStatusStr), L"%d", PartitionSizeChangeTableStatus); } if (InterleaveInformationTableStatus_1 == MAX_UINT8) { StrnCpyS(InterleaveInformationTableStatus_1Str, MAX_PCD_TABLE_STATUS_LENGTH, pTmpStr, MAX_PCD_TABLE_STATUS_LENGTH - 1); } else { UnicodeSPrint(InterleaveInformationTableStatus_1Str, sizeof(InterleaveInformationTableStatus_1Str), L"%d", (UINT32)InterleaveInformationTableStatus_1); } if (InterleaveInformationTableStatus_2 == MAX_UINT8) { StrnCpyS(InterleaveInformationTableStatus_2Str, MAX_PCD_TABLE_STATUS_LENGTH, pTmpStr, MAX_PCD_TABLE_STATUS_LENGTH - 1); } else { UnicodeSPrint(InterleaveInformationTableStatus_2Str, sizeof(InterleaveInformationTableStatus_2Str), L"%d", InterleaveInformationTableStatus_2); } FREE_POOL_SAFE(pTmpStr); } switch (CoutStatus) { case CONFIG_OUTPUT_STATUS_ERROR: if (PartitionSizeChangeTableStatus == PARTITION_SIZE_CHANGE_STATUS_FW_ERROR) { pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_PMEM_MODULE_FIRMWARE_ERROR), NULL); } else if (PartitionSizeChangeTableStatus == PARTITION_SIZE_CHANGE_STATUS_EXCEED_DRAM_DECODERS || InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_EXCEED_DRAM_DECODERS || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_EXCEED_DRAM_DECODERS) { pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_INSUFFICIENT_SILICON_RESOURCES), NULL); } else if (InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_EXCEED_MAX_SPA_SPACE || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_EXCEED_MAX_SPA_SPACE) { pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_INSUFFICIENT_SPA_SPACE), NULL); } else if (PartitionSizeChangeTableStatus == PARTITION_SIZE_CHANGE_STATUS_DIMM_MISSING || InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_DIMM_MISSING || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_DIMM_MISSING) { pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_PMEM_MODULE_MISSING_IN_ISET), NULL); } else if (PartitionSizeChangeTableStatus == PARTITION_SIZE_CHANGE_STATUS_ISET_MISSING || InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_ISET_MISSING || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_ISET_MISSING) { pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_MATCHING_ISET_NOT_FOUND), NULL); } else if (PartitionSizeChangeTableStatus == PARTITION_SIZE_CHANGE_STATUS_EXCEED_SIZE) { pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_EXCEEDS_PARTITION_SIZE), NULL); } else if (InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_CIN_MISSING || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_CIN_MISSING) { pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_CIN_MISSING), NULL); } else if (InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_CHANNEL_NOT_MATCH || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_CHANNEL_NOT_MATCH) { pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_CHANNEL_NOT_MATCH), NULL); } else if (PartitionSizeChangeTableStatus == PARTITION_SIZE_CHANGE_STATUS_UNSUPPORTED_ALIGNMENT || InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_UNSUPPORTED_ALIGNMENT || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_UNSUPPORTED_ALIGNMENT) { pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_UNSUPPORTED_ALIGNMENT), NULL); } else if (InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_REQUEST_UNSUPPORTED || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_REQUEST_UNSUPPORTED) { pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_REQUEST_UNSUPPORTED), NULL); } else { pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_UNKNOWN), NULL); } break; case CONFIG_OUTPUT_STATUS_CPU_MAX_MEMORY_LIMIT_VIOLATION: pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_CPU_MAX_MEMORY_LIMIT_VIOLATION), NULL); break; case CONFIG_OUTPUT_STATUS_NM_FM_RATIO_UNSUPPORTED: pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_NM_FM_RATIO_UNSUPPORTED), NULL); break; case CONFIG_OUTPUT_STATUS_POPULATION_ISSUE: pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_POPULATION_VIOLATION), NULL); break; default: pCoutErrorMessage = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_STATUS_UNKNOWN), NULL); break; } pTmpStr1 = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CONFIG_DIAG_COUT_CONFIG_DETAILED_STATUS), NULL); pReturnStr = CatSPrintClean(NULL, pTmpStr1, CoutStatus, pCoutErrorMessage, PartitionSizeChangeTableStatusStr, InterleaveInformationTableStatus_1Str, InterleaveInformationTableStatus_2Str); FREE_POOL_SAFE(pTmpStr1); FREE_POOL_SAFE(pCoutErrorMessage); NVDIMM_EXIT(); return pReturnStr; } /** Check CCUR table for a broken interleave set and update the list of broken interleave sets if one is discovered @param[in] pCurrentConfig, CCUR table to check for broken interleave set when DIMM(s) are misplaced @param[in] MissingDimm Flag to indicate if one of the DIMMs in an interleave set is missing @param[in out] pBrokenISs array of broken IS information to update @param[in out] pBrokenISCount, current number of discovered broken IS's in the array @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL **/ STATIC EFI_STATUS UpdateBrokenInterleaveSets( IN NVDIMM_CURRENT_CONFIG *pCurrentConfig, IN BOOLEAN MissingDimm, IN OUT BROKEN_IS *pBrokenISs, IN OUT UINT16 *pBrokenISCount ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_PARTITION_SIZE_CHANGE *pPartSizeChange = NULL; PCAT_TABLE_HEADER *pCurPcatTable = NULL; UINT32 SizeOfPcatTables = 0; DIMM *pDimm = NULL; UINT16 Index = 0; UINT16 BrokenISArrayIndex = 0; UINT16 DimmIdIndex = 0; BOOLEAN BrokenISFound = FALSE; BOOLEAN DimmIdFound = FALSE; BOOLEAN DimmLocationIssue = FALSE; BOOLEAN PcdRevision_1 = FALSE; ACPI_REVISION PcdRevision; UINT32 TmpDimmSerialNumber = 0; DIMM_UNIQUE_IDENTIFIER TmpDimmUid; DIMM_LOCATION DimmLocation; DIMM_LOCATION CurrentDimmLocation; PMTT_MODULE_INFO *pPmttModuleInfo = NULL; UINT8 NumOfDimmsInInterleaveSet = 0; UINT16 InterleaveSetIndex = 0; UINT16 InterleaveInfoHeaderLength = 0; VOID *pCurrentIdentInfo = NULL; NVDIMM_ENTRY(); if (pCurrentConfig == NULL || pBrokenISs == NULL || pBrokenISCount == NULL || *pBrokenISCount > MAX_IS_CONFIGS) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("Invalid Parameter"); goto Finish; } if (IS_ACPI_HEADER_REV_INVALID(pCurrentConfig)) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("Error: Invalid revision value %d for PCD Current Config table.", pCurrentConfig->Header.Revision.AsUint8); goto Finish; } ZeroMem(&TmpDimmUid, sizeof(TmpDimmUid)); PcdRevision_1 = (pCurrentConfig->Header.Revision.AsUint8 == NVDIMM_CONFIGURATION_TABLES_REVISION_1); PcdRevision.AsUint8 = pCurrentConfig->Header.Revision.AsUint8; // For DIMM misplaced or re-ordering issue use DIMM location data in CCUR (PCD Rev 1.1) for validation if (!MissingDimm && !IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdRevision)) { NVDIMM_DBG("DIMM Location details of any interleave set in CCUR table not available."); goto Finish; } /** Check if there is at least one PCAT table **/ if (pCurrentConfig->Header.Length <= sizeof(*pCurrentConfig)) { goto Finish; } pCurPcatTable = (PCAT_TABLE_HEADER *)&pCurrentConfig->pPcatTables; SizeOfPcatTables = pCurrentConfig->Header.Length - (UINT32)((UINT8 *)pCurPcatTable - (UINT8 *)pCurrentConfig); while ((UINT32)((UINT8 *)pCurPcatTable - (UINT8 *)&pCurrentConfig->pPcatTables) < SizeOfPcatTables) { if (pCurPcatTable->Type == PCAT_TYPE_PARTITION_SIZE_CHANGE_TABLE) { pPartSizeChange = (NVDIMM_PARTITION_SIZE_CHANGE *)pCurPcatTable; pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pPartSizeChange->Header.Length); } else if (pCurPcatTable->Type == PCAT_TYPE_INTERLEAVE_INFORMATION_TABLE) { if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcdRevision)) { NVDIMM_INTERLEAVE_INFORMATION *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION *)pCurPcatTable; pCurrentIdentInfo = (NVDIMM_IDENTIFICATION_INFORMATION *)&pInterleaveInfo->pIdentificationInfoList; NumOfDimmsInInterleaveSet = pInterleaveInfo->NumOfDimmsInInterleaveSet; InterleaveSetIndex = pInterleaveInfo->InterleaveSetIndex; InterleaveInfoHeaderLength = pInterleaveInfo->Header.Length; } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdRevision)) { NVDIMM_INTERLEAVE_INFORMATION3 *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION3 *)pCurPcatTable; pCurrentIdentInfo = (NVDIMM_IDENTIFICATION_INFORMATION3 *)&pInterleaveInfo->pIdentificationInfoList; NumOfDimmsInInterleaveSet = pInterleaveInfo->NumOfDimmsInInterleaveSet; InterleaveSetIndex = pInterleaveInfo->InterleaveSetIndex; InterleaveInfoHeaderLength = pInterleaveInfo->Header.Length; } // for each dimm identifier in the table for (Index = 0; Index < NumOfDimmsInInterleaveSet; Index++) { if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcdRevision)) { NVDIMM_IDENTIFICATION_INFORMATION *pCurrentIdentInfoTemp = (NVDIMM_IDENTIFICATION_INFORMATION *)pCurrentIdentInfo; TmpDimmSerialNumber = pCurrentIdentInfoTemp->DimmIdentification.Version1.DimmSerialNumber; CopyMem_S(&TmpDimmUid, sizeof(DIMM_UNIQUE_IDENTIFIER), &(pCurrentIdentInfoTemp->DimmIdentification.Version2.Uid), sizeof(DIMM_UNIQUE_IDENTIFIER)); } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdRevision)) { NVDIMM_IDENTIFICATION_INFORMATION3 *pCurrentIdentInfoTemp = (NVDIMM_IDENTIFICATION_INFORMATION3 *)pCurrentIdentInfo; CopyMem_S(&TmpDimmUid, sizeof(DIMM_UNIQUE_IDENTIFIER), &(pCurrentIdentInfoTemp->DimmIdentification), sizeof(DIMM_UNIQUE_IDENTIFIER)); CopyMem_S(&DimmLocation, sizeof(DIMM_LOCATION), &(pCurrentIdentInfoTemp->DimmLocation), sizeof(DIMM_LOCATION)); } if (PcdRevision_1) { pDimm = GetDimmBySerialNumber(&gNvmDimmData->PMEMDev.Dimms, TmpDimmSerialNumber); } else { pDimm = GetDimmByUniqueIdentifier(&gNvmDimmData->PMEMDev.Dimms, TmpDimmUid); } if (!MissingDimm) { if (pDimm != NULL) { pPmttModuleInfo = GetDimmModuleByPidFromPmtt(pDimm->DimmID, gNvmDimmData->PMEMDev.pPmttHead); if (pPmttModuleInfo == NULL) { NVDIMM_DBG("DIMM Module not found in PMTT"); } } else { NVDIMM_DBG("DIMM ID missing from DIMM Inventory. Incorrect Config Status value!"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } DimmLocationIssue = FALSE; //Compare the Identification Info DIMM location with the current DIMM location in PMTT if (pPmttModuleInfo != NULL) { if ((DimmLocation.Split.SocketId != pPmttModuleInfo->SocketId) || (DimmLocation.Split.DieId != pPmttModuleInfo->DieId) || (DimmLocation.Split.MemControllerId != pPmttModuleInfo->MemControllerId) || (DimmLocation.Split.ChannelId != pPmttModuleInfo->ChannelId) || (DimmLocation.Split.SlotId != pPmttModuleInfo->SlotId)) { DimmLocationIssue = TRUE; CurrentDimmLocation.Split.SocketId = pPmttModuleInfo->SocketId; CurrentDimmLocation.Split.DieId = pPmttModuleInfo->DieId; CurrentDimmLocation.Split.MemControllerId = pPmttModuleInfo->MemControllerId; CurrentDimmLocation.Split.ChannelId = pPmttModuleInfo->ChannelId; CurrentDimmLocation.Split.SlotId = pPmttModuleInfo->SlotId; } } //Compare the Identification Info DIMM location with the current DIMM location in NFIT if PMTT not present else { if ((DimmLocation.Split.SocketId != pDimm->SocketId) || (DimmLocation.Split.DieId != MAX_DIEID_SINGLE_DIE_SOCKET) || (DimmLocation.Split.MemControllerId != pDimm->ImcId) || (DimmLocation.Split.ChannelId != pDimm->ChannelId) || (DimmLocation.Split.SlotId != pDimm->ChannelPos)) { DimmLocationIssue = TRUE; CurrentDimmLocation.Split.SocketId = pDimm->SocketId; CurrentDimmLocation.Split.DieId = MAX_DIEID_SINGLE_DIE_SOCKET; CurrentDimmLocation.Split.MemControllerId = pDimm->ImcId; CurrentDimmLocation.Split.ChannelId = pDimm->ChannelId; CurrentDimmLocation.Split.SlotId = pDimm->ChannelPos; } } } // If a certain dimm identifier is missing from DimmInventory or not in the right order // for a given Interleave Table, mark it as a bad Interleave set index if (pDimm == NULL || DimmLocationIssue) { BrokenISFound = FALSE; // Check and see if index is already in the bad list for (BrokenISArrayIndex = 0; BrokenISArrayIndex < *pBrokenISCount; BrokenISArrayIndex++) { if (pBrokenISs[BrokenISArrayIndex].InterleaveSetIndex == InterleaveSetIndex) { BrokenISFound = TRUE; break; } } // if yes check and see if serial number is in the bad struct and add it if not if (BrokenISFound) { DimmIdFound = FALSE; for (DimmIdIndex = 0; DimmIdIndex < pBrokenISs[BrokenISArrayIndex].MissingDimmCount; DimmIdIndex++) { if (((PcdRevision_1) && (TmpDimmSerialNumber == pBrokenISs[BrokenISArrayIndex].MissingDimmIdentifier[DimmIdIndex].SerialNumber)) || ((!PcdRevision_1) && (0 == CompareMem(&TmpDimmUid, &pBrokenISs[BrokenISArrayIndex].MissingDimmIdentifier[DimmIdIndex], sizeof(DIMM_UNIQUE_IDENTIFIER))))) { DimmIdFound = TRUE; break; } } if (!DimmIdFound) { if (PcdRevision_1) { pBrokenISs[BrokenISArrayIndex].MissingDimmIdentifier[pBrokenISs[BrokenISArrayIndex].MissingDimmCount].SerialNumber = TmpDimmSerialNumber; } else { CopyMem_S(&pBrokenISs[BrokenISArrayIndex].MissingDimmIdentifier[pBrokenISs[BrokenISArrayIndex].MissingDimmCount], sizeof(DIMM_UNIQUE_IDENTIFIER), &TmpDimmUid, sizeof(DIMM_UNIQUE_IDENTIFIER)); } if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdRevision)) { CopyMem_S(&pBrokenISs[BrokenISArrayIndex].MisplacedDimmLocations[pBrokenISs[BrokenISArrayIndex].MissingDimmCount], sizeof(DIMM_LOCATION), &DimmLocation, sizeof(DIMM_LOCATION)); if (!MissingDimm) { CopyMem_S(&pBrokenISs[BrokenISArrayIndex].CurrentDimmLocations[pBrokenISs[BrokenISArrayIndex].MissingDimmCount], sizeof(DIMM_LOCATION), &CurrentDimmLocation, sizeof(DIMM_LOCATION)); } } pBrokenISs[BrokenISArrayIndex].MissingDimmCount++; } // if no add to the next empty index } else { pBrokenISs[*pBrokenISCount].InterleaveSetIndex = InterleaveSetIndex; if (PcdRevision_1) { pBrokenISs[*pBrokenISCount].MissingDimmIdentifier[pBrokenISs[BrokenISArrayIndex].MissingDimmCount].SerialNumber = TmpDimmSerialNumber; } else { CopyMem_S(&pBrokenISs[*pBrokenISCount].MissingDimmIdentifier[pBrokenISs[BrokenISArrayIndex].MissingDimmCount], sizeof(DIMM_UNIQUE_IDENTIFIER), &TmpDimmUid, sizeof(DIMM_UNIQUE_IDENTIFIER)); } if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdRevision)) { CopyMem_S(&pBrokenISs[*pBrokenISCount].MisplacedDimmLocations[pBrokenISs[BrokenISArrayIndex].MissingDimmCount], sizeof(DIMM_LOCATION), &DimmLocation, sizeof(DIMM_LOCATION)); if (!MissingDimm) { CopyMem_S(&pBrokenISs[*pBrokenISCount].CurrentDimmLocations[pBrokenISs[BrokenISArrayIndex].MissingDimmCount], sizeof(DIMM_LOCATION), &CurrentDimmLocation, sizeof(DIMM_LOCATION)); } } pBrokenISs[*pBrokenISCount].MissingDimmCount++; *pBrokenISCount = *pBrokenISCount + 1; } } if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcdRevision)) { pCurrentIdentInfo = (UINT8 *)pCurrentIdentInfo + sizeof(NVDIMM_IDENTIFICATION_INFORMATION); } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdRevision)) { pCurrentIdentInfo = (UINT8 *)pCurrentIdentInfo + sizeof(NVDIMM_IDENTIFICATION_INFORMATION3); } } pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, InterleaveInfoHeaderLength); } else { NVDIMM_DBG("Unknown table discovered in CCUR"); break; } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check the platform configuration data for errors @param[in] pDimm Pointer to the DIMM @param[in] DimmCount DIMMs count @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[in out] ppResult Pointer to the result string of platform config diagnostics message @param[out] pDiagState Pointer to the platform config diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL @retval EFI_NOT_FOUND Unable to locate relevant PCAT tables. **/ STATIC EFI_STATUS CheckPlatformConfigurationData( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT8 DimmIdPreference, IN OUT CHAR16 **ppResultStr, OUT UINT8 *pDiagState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 *pDetailedStatusStr = NULL; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; CHAR16 DimmUid[MAX_DIMM_UID_LENGTH]; NVDIMM_CONFIGURATION_HEADER *pPcdConfHeader = NULL; NVDIMM_CURRENT_CONFIG *pPcdCurrentConf = NULL; NVDIMM_PLATFORM_CONFIG_OUTPUT *pPcdOutputConf = NULL; NVDIMM_PLATFORM_CONFIG_INPUT *pPcdInputConf = NULL; UINT32 PartitionSizeChangeTableStatus = MAX_UINT32; UINT8 InterleaveInformationTableStatus_1 = MAX_UINT8; UINT8 InterleaveInformationTableStatus_2 = MAX_UINT8; UINT16 Index = 0; UINT16 Index2 = 0; UINT16 BrokenISCount = 0; BROKEN_IS *pBrokenISs = NULL; CHAR16 *pTmpDimmIdStr = NULL; ACPI_REVISION PcdRevision; enum PcdErrorTypes { PcdSuccess = 0, PcdErrorGoalData, PcdErrorInsufficientResources, PcdErrorFirmware, PcdErrorMissingDimm, PcdErrorDimmLocationIssue, PcdErrorCurConfig, PcdErrorUnknown, } PcdErrorType; PcdErrorType = PcdSuccess; NVDIMM_ENTRY(); ZeroMem(DimmStr, sizeof(DimmStr)); ZeroMem(DimmUid, sizeof(DimmUid)); ZeroMem(&PcdRevision, sizeof(PcdRevision)); if (DimmCount == 0 || ppDimms == NULL || DimmCount > MAX_DIMMS || ppResultStr == NULL || pDiagState == NULL) { ReturnCode = EFI_INVALID_PARAMETER; if (pDiagState != NULL) { *pDiagState |= DIAG_STATE_MASK_ABORTED; } goto Finish; } pBrokenISs = AllocateZeroPool(sizeof(*pBrokenISs) * MAX_IS_CONFIGS); if (pBrokenISs == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; *pDiagState |= DIAG_STATE_MASK_ABORTED; goto Finish; } for (Index = 0; Index < DimmCount; ++Index) { ReturnCode = GetDimmUid(ppDimms[Index], DimmUid, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("GetDimmUid function for DIMM ID 0x%x failed.", ppDimms[Index]->DeviceHandle.AsUint32); continue; } ReturnCode = GetPreferredValueAsString(ppDimms[Index]->DeviceHandle.AsUint32, DimmUid, DimmIdPreference == DISPLAY_DIMM_ID_HANDLE, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("GetPreferredValueAsString function for DIMM ID 0x%x failed.", ppDimms[Index]->DeviceHandle.AsUint32); continue; } ReturnCode = GetPlatformConfigDataOemPartition(ppDimms[Index], FALSE, &pPcdConfHeader); #ifdef MEMORY_CORRUPTION_WA if (ReturnCode == EFI_DEVICE_ERROR) { ReturnCode = GetPlatformConfigDataOemPartition(ppDimms[Index], FALSE, &pPcdConfHeader); } #endif // MEMORY_CORRUPTION_WA if (!EFI_ERROR(ReturnCode)) { if (pPcdConfHeader->CurrentConfStartOffset == 0 || pPcdConfHeader->CurrentConfDataSize == 0) { // Dimm not configured APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_CONFIG_DIMM_NOT_CONFIGURED), EVENT_CODE_606, DIAG_STATE_MASK_OK, ppResultStr, pDiagState, DimmStr); FREE_POOL_SAFE(pPcdConfHeader); NVDIMM_WARN("There is no Current Config table"); continue; } pPcdCurrentConf = GET_NVDIMM_CURRENT_CONFIG(pPcdConfHeader); if (!IsPcdCurrentConfHeaderValid(pPcdCurrentConf, ppDimms[Index]->PcdOemPartitionSize)) { ReturnCode = EFI_VOLUME_CORRUPTED; } PcdRevision.AsUint8 = pPcdCurrentConf->Header.Revision.AsUint8; } if (!EFI_ERROR(ReturnCode)) { // Check for any errors in PCD CCUR switch (pPcdCurrentConf->ConfigStatus) { case DIMM_CONFIG_SUCCESS: PcdErrorType = PcdSuccess; break; case DIMM_CONFIG_IS_INCOMPLETE: PcdErrorType = PcdErrorMissingDimm; break; case DIMM_CONFIG_NO_MATCHING_IS: case DIMM_CONFIG_NEW_DIMM: PcdErrorType = PcdErrorDimmLocationIssue; break; case DIMM_CONFIG_DCPMM_POPULATION_ISSUE: case DIMM_CONFIG_PM_MAPPED_VM_POPULATION_ISSUE: case DIMM_CONFIG_DCPMM_NM_FM_RATIO_UNSUPPORTED: case DIMM_CONFIG_CPU_MAX_MEMORY_LIMIT_VIOLATION: PcdErrorType = PcdErrorCurConfig; break; } if (PcdErrorType != PcdSuccess) { goto ProcessPcdError; } if (pPcdConfHeader->ConfInputStartOffset == 0 || pPcdConfHeader->ConfInputDataSize == 0) { FREE_POOL_SAFE(pPcdConfHeader); NVDIMM_WARN("There is no Input Config table"); continue; } pPcdInputConf = GET_NVDIMM_PLATFORM_CONFIG_INPUT(pPcdConfHeader); if (!IsPcdConfInputHeaderValid(pPcdInputConf, ppDimms[Index]->PcdOemPartitionSize)) { ReturnCode = EFI_VOLUME_CORRUPTED; } else if (pPcdConfHeader->ConfOutputStartOffset == 0 || pPcdConfHeader->ConfOutputDataSize == 0) { // No Output table defined yet means the goal has not been applied yet APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_CONFIG_GOAL_NOT_APPLIED), EVENT_CODE_609, DIAG_STATE_MASK_OK, ppResultStr, pDiagState, DimmStr); } } if (!EFI_ERROR(ReturnCode)) { if (pPcdConfHeader->ConfOutputStartOffset == 0 || pPcdConfHeader->ConfOutputDataSize == 0) { FREE_POOL_SAFE(pPcdConfHeader); NVDIMM_WARN("There is no Output Config table"); continue; } pPcdOutputConf = GET_NVDIMM_PLATFORM_CONFIG_OUTPUT(pPcdConfHeader); if (!IsPcdConfOutputHeaderValid(pPcdOutputConf, ppDimms[Index]->PcdOemPartitionSize)) { ReturnCode = EFI_VOLUME_CORRUPTED; } else if (pPcdOutputConf->SequenceNumber != pPcdInputConf->SequenceNumber) { // The goal has not been applied yet APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_CONFIG_GOAL_NOT_APPLIED), EVENT_CODE_609, DIAG_STATE_MASK_OK, ppResultStr, pDiagState, DimmStr); } } if (EFI_ERROR(ReturnCode)) { APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_CONFIG_INVALID_PCD_DATA), EVENT_CODE_621, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, DimmStr); continue; } GetPlatformConfigOutputPCATStatus( pPcdOutputConf, &PartitionSizeChangeTableStatus, &InterleaveInformationTableStatus_1, &InterleaveInformationTableStatus_2); // Check for any errors in applying the goal request (PCD CIN) switch (pPcdCurrentConf->ConfigStatus) { case DIMM_CONFIG_SUCCESS: PcdErrorType = PcdSuccess; break; case DIMM_CONFIG_OLD_CONFIG_USED: case DIMM_CONFIG_BAD_CONFIG: if (pPcdOutputConf->ValidationStatus == CONFIG_OUTPUT_STATUS_ERROR) { if (PartitionSizeChangeTableStatus == PARTITION_SIZE_CHANGE_STATUS_FW_ERROR) { PcdErrorType = PcdErrorFirmware; } else if (PartitionSizeChangeTableStatus == PARTITION_SIZE_CHANGE_STATUS_EXCEED_DRAM_DECODERS || InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_EXCEED_DRAM_DECODERS || InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_EXCEED_MAX_SPA_SPACE || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_EXCEED_DRAM_DECODERS || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_EXCEED_MAX_SPA_SPACE) { PcdErrorType = PcdErrorInsufficientResources; } else if (PartitionSizeChangeTableStatus == PARTITION_SIZE_CHANGE_STATUS_DIMM_MISSING || PartitionSizeChangeTableStatus == PARTITION_SIZE_CHANGE_STATUS_ISET_MISSING || PartitionSizeChangeTableStatus == PARTITION_SIZE_CHANGE_STATUS_EXCEED_SIZE || PartitionSizeChangeTableStatus == PARTITION_SIZE_CHANGE_STATUS_UNSUPPORTED_ALIGNMENT || InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_DIMM_MISSING || InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_ISET_MISSING || InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_CIN_MISSING || InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_REQUEST_UNSUPPORTED || InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_CHANNEL_NOT_MATCH || InterleaveInformationTableStatus_1 == INTERLEAVE_INFO_STATUS_UNSUPPORTED_ALIGNMENT || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_DIMM_MISSING || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_ISET_MISSING || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_CIN_MISSING || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_REQUEST_UNSUPPORTED || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_CHANNEL_NOT_MATCH || InterleaveInformationTableStatus_2 == INTERLEAVE_INFO_STATUS_UNSUPPORTED_ALIGNMENT) { PcdErrorType = PcdErrorGoalData; } else { PcdErrorType = PcdErrorUnknown; } } else if (pPcdOutputConf->ValidationStatus == CONFIG_OUTPUT_STATUS_CPU_MAX_MEMORY_LIMIT_VIOLATION || pPcdOutputConf->ValidationStatus == CONFIG_OUTPUT_STATUS_NM_FM_RATIO_UNSUPPORTED || pPcdOutputConf->ValidationStatus == CONFIG_OUTPUT_STATUS_POPULATION_ISSUE) { PcdErrorType = PcdErrorGoalData; } else { PcdErrorType = PcdErrorUnknown; } break; case DIMM_CONFIG_IN_CHECKSUM_NOT_VALID: case DIMM_CONFIG_REVISION_NOT_SUPPORTED: PcdErrorType = PcdErrorGoalData; break; default: PcdErrorType = PcdErrorUnknown; } ProcessPcdError: // Print Message based on PcdError switch (PcdErrorType) { case PcdSuccess: break; case PcdErrorFirmware: pDetailedStatusStr = GetCoutDetailedStatusStr(pPcdOutputConf->ValidationStatus, PartitionSizeChangeTableStatus, InterleaveInformationTableStatus_1, InterleaveInformationTableStatus_2); APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_CONFIG_GOAL_FAILED_FIRMWARE), EVENT_CODE_626, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, DimmStr, pDetailedStatusStr); FREE_POOL_SAFE(pDetailedStatusStr); break; case PcdErrorGoalData: pDetailedStatusStr = GetCoutDetailedStatusStr(pPcdOutputConf->ValidationStatus, PartitionSizeChangeTableStatus, InterleaveInformationTableStatus_1, InterleaveInformationTableStatus_2); APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_CONFIG_GOAL_FAILED_DATA), EVENT_CODE_624, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, DimmStr, pDetailedStatusStr); FREE_POOL_SAFE(pDetailedStatusStr); break; case PcdErrorCurConfig: pDetailedStatusStr = GetCCurDetailedStatusStr(pPcdCurrentConf->ConfigStatus); APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_CURRENT_CONFIG_FAILED_DATA), EVENT_CODE_633, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, DimmStr, pDetailedStatusStr); FREE_POOL_SAFE(pDetailedStatusStr); break; case PcdErrorInsufficientResources: pDetailedStatusStr = GetCoutDetailedStatusStr(pPcdOutputConf->ValidationStatus, PartitionSizeChangeTableStatus, InterleaveInformationTableStatus_1, InterleaveInformationTableStatus_2); APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_CONFIG_GOAL_FAILED_INSUFFICIENT_RESOURCES), EVENT_CODE_625, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, DimmStr, pDetailedStatusStr); FREE_POOL_SAFE(pDetailedStatusStr); break; case PcdErrorMissingDimm: ReturnCode = UpdateBrokenInterleaveSets(pPcdCurrentConf, TRUE, pBrokenISs, &BrokenISCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to update broken interleave sets"); *pDiagState |= DIAG_STATE_MASK_ABORTED; goto Finish; } break; case PcdErrorDimmLocationIssue: ReturnCode = UpdateBrokenInterleaveSets(pPcdCurrentConf, FALSE, pBrokenISs, &BrokenISCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to update broken interleave sets"); *pDiagState |= DIAG_STATE_MASK_ABORTED; goto Finish; } break; case PcdErrorUnknown: default: pDetailedStatusStr = GetCoutDetailedStatusStr(pPcdOutputConf->ValidationStatus, PartitionSizeChangeTableStatus, InterleaveInformationTableStatus_1, InterleaveInformationTableStatus_2); APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_CONFIG_GOAL_FAILED_UNKNOWN), EVENT_CODE_627, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, DimmStr, pDetailedStatusStr); FREE_POOL_SAFE(pDetailedStatusStr); break; } FREE_POOL_SAFE(pPcdConfHeader); } for (Index = 0; Index < BrokenISCount; Index++) { if (pBrokenISs[Index].MissingDimmCount > 0) { for (Index2 = 0; Index2 < pBrokenISs[Index].MissingDimmCount; Index2++) { if (IS_ACPI_REV_MAJ_0_MIN_2(PcdRevision)) { pTmpDimmIdStr = CatSPrint(NULL, L"%04x-%02x-%04x-%08x", EndianSwapUint16(pBrokenISs[Index].MissingDimmIdentifier[Index2].ManufacturerId), pBrokenISs[Index].MissingDimmIdentifier[Index2].ManufacturingLocation, EndianSwapUint16(pBrokenISs[Index].MissingDimmIdentifier[Index2].ManufacturingDate), EndianSwapUint32(pBrokenISs[Index].MissingDimmIdentifier[Index2].SerialNumber)); } else { pTmpDimmIdStr = CatSPrint(NULL, L"0x%08x", EndianSwapUint32(pBrokenISs[Index].MissingDimmIdentifier[Index2].SerialNumber)); } if (PcdErrorType == PcdErrorMissingDimm) { if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdRevision)) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_CONFIG_IS_BROKEN_DIMMS_MISSING_LOCATION), EVENT_CODE_631, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pBrokenISs[Index].InterleaveSetIndex, pTmpDimmIdStr, pBrokenISs[Index].MisplacedDimmLocations[Index2].Split.SocketId, pBrokenISs[Index].MisplacedDimmLocations[Index2].Split.DieId, pBrokenISs[Index].MisplacedDimmLocations[Index2].Split.MemControllerId, pBrokenISs[Index].MisplacedDimmLocations[Index2].Split.ChannelId, pBrokenISs[Index].MisplacedDimmLocations[Index2].Split.SlotId); } else { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_CONFIG_IS_BROKEN_DIMMS_MISSING), EVENT_CODE_628, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pBrokenISs[Index].InterleaveSetIndex, pTmpDimmIdStr); } } if (PcdErrorType == PcdErrorDimmLocationIssue && (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdRevision))) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_CONFIG_IS_BROKEN_DIMMS_MISPLACED_LOCATION), EVENT_CODE_632, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pBrokenISs[Index].InterleaveSetIndex, pTmpDimmIdStr, pBrokenISs[Index].CurrentDimmLocations[Index2].Split.SocketId, pBrokenISs[Index].CurrentDimmLocations[Index2].Split.DieId, pBrokenISs[Index].CurrentDimmLocations[Index2].Split.MemControllerId, pBrokenISs[Index].CurrentDimmLocations[Index2].Split.ChannelId, pBrokenISs[Index].CurrentDimmLocations[Index2].Split.SlotId, pBrokenISs[Index].MisplacedDimmLocations[Index2].Split.SocketId, pBrokenISs[Index].MisplacedDimmLocations[Index2].Split.DieId, pBrokenISs[Index].MisplacedDimmLocations[Index2].Split.MemControllerId, pBrokenISs[Index].MisplacedDimmLocations[Index2].Split.ChannelId, pBrokenISs[Index].MisplacedDimmLocations[Index2].Split.SlotId); } FREE_POOL_SAFE(pTmpDimmIdStr); } } } Finish: FREE_POOL_SAFE(pPcdConfHeader); FREE_POOL_SAFE(pBrokenISs); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check for uninitialized dimms in the system @param[in out] ppResult Pointer to the result string of platform config diagnostics message @param[out] pDiagState Pointer to the platform config diagnostics test state @param DimmIdPreference Dimm id preference value @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL @retval EFI_NOT_FOUND Unable to locate relevant PCAT tables. **/ STATIC EFI_STATUS CheckUninitializedDimms( IN OUT CHAR16 **ppResultStr, OUT UINT8 *pDiagState, IN UINT8 DimmIdPreference ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LIST_ENTRY *pNode = NULL; DIMM *pCurDimm = NULL; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; CHAR16 DimmUid[MAX_DIMM_UID_LENGTH]; NVDIMM_ENTRY(); if (ppResultStr == NULL || pDiagState == NULL) { ReturnCode = EFI_INVALID_PARAMETER; if (pDiagState != NULL) { *pDiagState |= DIAG_STATE_MASK_ABORTED; } goto Finish; } LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Dimms) { pCurDimm = DIMM_FROM_NODE(pNode); if (pCurDimm->NonFunctional == FALSE) { continue; } ZeroMem(DimmStr, sizeof(DimmStr)); ZeroMem(DimmUid, sizeof(DimmUid)); ReturnCode = GetDimmUid(pCurDimm, DimmUid, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("GetDimmUid function for DIMM ID 0x%x failed.", pCurDimm->DeviceHandle.AsUint32); continue; } ReturnCode = GetPreferredValueAsString(pCurDimm->DeviceHandle.AsUint32, DimmUid, DimmIdPreference == DISPLAY_DIMM_ID_HANDLE, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("GetPreferredValueAsString function for DIMM ID 0x%x failed.", pCurDimm->DeviceHandle.AsUint32); continue; } APPEND_RESULT_TO_THE_LOG(pCurDimm, STRING_TOKEN(STR_CONFIG_DIMM_FAILED_TO_INITIALIZE), EVENT_CODE_618, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, DimmStr); } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check the various supported system capabilities @param[in] pDimm Pointer to the DIMM @param[in] DimmCount DIMMs count @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[in out] ppResult Pointer to the result string of platform config diagnostics message @param[out] pDiagState Pointer to the platform config diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL @retval EFI_NOT_FOUND Unable to locate relevant PCAT tables. **/ STATIC EFI_STATUS CheckSystemSupportedCapabilities( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT8 DimmIdPreference, IN OUT CHAR16 **ppResultStr, OUT UINT8 *pDiagState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; BOOLEAN ConfigChangeSupported = FALSE; NVDIMM_ENTRY(); if (DimmCount == 0 || ppDimms == NULL || DimmCount > MAX_DIMMS || ppResultStr == NULL || pDiagState == NULL) { ReturnCode = EFI_INVALID_PARAMETER; if (pDiagState != NULL) { *pDiagState |= DIAG_STATE_MASK_ABORTED; } goto Finish; } /** Check if BIOS supports changing configuration through management software **/ ReturnCode = CheckIfBiosSupportsConfigChange(&ConfigChangeSupported); if (ReturnCode == EFI_LOAD_ERROR) { *pDiagState |= DIAG_STATE_MASK_ABORTED; goto Finish; } else if (!ConfigChangeSupported) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_CONFIG_NO_OS_PROVISIONING), EVENT_CODE_623, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState); goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check for duplicate Dimm UID values in the platform, and accordingly append to the platform config diagnostics result. Also, accordingly modifies the test-state. @param[in] pDimm Pointer to the DIMM @param[in] DimmCount DIMMs count @param[in out] ppResult Pointer to the result string of platform config diagnostics message @param[out] pDiagState Pointer to the platform config diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL @retval EFI_OUT_OF_RESOURCES when memory allocation fails. **/ STATIC EFI_STATUS CheckDimmUIDDuplication( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN OUT CHAR16 **ppResultStr, OUT UINT8 *pDiagState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT16 PossibleNumDuplicateUID = 0; UINT16 NumDuplicateUID = 0; UINT16 TotalNumDuplicateUID = 0; CHAR16 **ppDuplicateDimmUids = NULL; UINT32 Index = 0; UINT32 TestUIDIndex = 0; UINT32 DuplicateUIDIndex = 0; CHAR16 CandidateDimmUID[MAX_DIMM_UID_LENGTH]; CHAR16 TestDimmUID[MAX_DIMM_UID_LENGTH]; BOOLEAN DuplicateFound = FALSE; NVDIMM_ENTRY(); ZeroMem(CandidateDimmUID, sizeof(CandidateDimmUID)); ZeroMem(TestDimmUID, sizeof(TestDimmUID)); if (DimmCount == 0 || ppDimms == NULL || DimmCount > MAX_DIMMS || ppResultStr == NULL || pDiagState == NULL) { ReturnCode = EFI_INVALID_PARAMETER; if (pDiagState != NULL) { *pDiagState |= DIAG_STATE_MASK_ABORTED; } goto Finish; } PossibleNumDuplicateUID = DimmCount / 2; ppDuplicateDimmUids = AllocateZeroPool(sizeof(*ppDuplicateDimmUids) * PossibleNumDuplicateUID); if (ppDuplicateDimmUids == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; *pDiagState |= DIAG_STATE_MASK_ABORTED; goto Finish; } for (Index = 0; Index < DimmCount; Index++) { DuplicateFound = FALSE; NumDuplicateUID = 1; ReturnCode = GetDimmUid(ppDimms[Index], CandidateDimmUID, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("GetDimmUid function for DIMM ID 0x%x failed.", ppDimms[Index]->DeviceHandle.AsUint32); *pDiagState |= DIAG_STATE_MASK_ABORTED; goto Finish; } for (DuplicateUIDIndex = 0; DuplicateUIDIndex < TotalNumDuplicateUID; DuplicateUIDIndex++) { if (StrICmp(CandidateDimmUID, ppDuplicateDimmUids[DuplicateUIDIndex]) == 0) { DuplicateFound = TRUE; break; } } if (DuplicateFound) { continue; } for (TestUIDIndex = Index + 1; TestUIDIndex < DimmCount; TestUIDIndex++) { ZeroMem(TestDimmUID, sizeof(TestDimmUID)); ReturnCode = GetDimmUid(ppDimms[TestUIDIndex], TestDimmUID, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("GetDimmUid function for DIMM ID 0x%x failed.", ppDimms[TestUIDIndex]->DeviceHandle.AsUint32); *pDiagState |= DIAG_STATE_MASK_ABORTED; goto Finish; } if (StrICmp(TestDimmUID, CandidateDimmUID) == 0) { NumDuplicateUID++; } } if (NumDuplicateUID > 1) { ppDuplicateDimmUids[TotalNumDuplicateUID] = AllocateZeroPool(MAX_DIMM_UID_LENGTH * sizeof(CHAR16)); if (ppDuplicateDimmUids[TotalNumDuplicateUID] == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; *pDiagState |= DIAG_STATE_MASK_ABORTED; goto Finish; } CopyMem_S(ppDuplicateDimmUids[TotalNumDuplicateUID], MAX_DIMM_UID_LENGTH * sizeof(CHAR16), CandidateDimmUID, MAX_DIMM_UID_LENGTH * sizeof(CHAR16)); TotalNumDuplicateUID++; APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_CONFIG_DUPLICATE_DIMM_UID), EVENT_CODE_608, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, NumDuplicateUID, CandidateDimmUID); } } Finish: if (ppDuplicateDimmUids != NULL) { for (DuplicateUIDIndex = 0; DuplicateUIDIndex < PossibleNumDuplicateUID; DuplicateUIDIndex++) { FREE_POOL_SAFE(ppDuplicateDimmUids[DuplicateUIDIndex]); } } FREE_POOL_SAFE(ppDuplicateDimmUids); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Run platform configuration diagnostics for the list of DIMMs, and appropriately populate the result structure. @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[out] pResult Pointer of structure with diagnostics test result @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS RunConfigDiagnostics( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT8 DimmIdPreference, OUT DIAG_INFO *pResult ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; SYSTEM_CAPABILITIES_INFO SysCapInfo; NVDIMM_ENTRY(); // Clear the pointer before using the struct SysCapInfo.PtrInterleaveFormatsSupported = 0; SysCapInfo.PtrInterleaveSize = 0; if (pResult == NULL || DimmCount > MAX_DIMMS) { NVDIMM_DBG("The platform configuration diagnostics test aborted due to an internal error."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (DimmCount == 0 || ppDimms == NULL || GetManageableDimmsCount(ppDimms, DimmCount) == 0) { ReturnCode = EFI_SUCCESS; APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_CONFIG_NO_MANAGEABLE_DIMMS), EVENT_CODE_601, DIAG_STATE_MASK_OK, &pResult->Message, &pResult->StateVal); goto Finish; } pResult->SubTestName[DIMMSPECS_TEST_INDEX] = CatSPrint(NULL, L"PMem module specs"); ReturnCode = CheckUninitializedDimms(&pResult->SubTestMessage[DIMMSPECS_TEST_INDEX], &pResult->SubTestStateVal[DIMMSPECS_TEST_INDEX],DimmIdPreference); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("The check for uninitialized dimms failed."); if ((pResult->SubTestStateVal[DIMMSPECS_TEST_INDEX] & DIAG_STATE_MASK_ABORTED) != 0) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_CONFIG_ABORTED_INTERNAL_ERROR), EVENT_CODE_630, DIAG_STATE_MASK_ABORTED, &pResult->SubTestMessage[DIMMSPECS_TEST_INDEX], &pResult->SubTestStateVal[DIMMSPECS_TEST_INDEX]); goto Finish; } } pResult->SubTestName[DUPLICATE_DIMM_TEST_INDEX] = CatSPrint(NULL, L"Duplicate PMem module"); ReturnCode = CheckDimmUIDDuplication(ppDimms, DimmCount, &pResult->SubTestMessage[DUPLICATE_DIMM_TEST_INDEX], &pResult->SubTestStateVal[1]); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("The check for duplicate UID numbers failed."); if ((pResult->SubTestStateVal[DUPLICATE_DIMM_TEST_INDEX] & DIAG_STATE_MASK_ABORTED) != 0) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_CONFIG_ABORTED_INTERNAL_ERROR), EVENT_CODE_630, DIAG_STATE_MASK_ABORTED, &pResult->SubTestMessage[DUPLICATE_DIMM_TEST_INDEX], &pResult->SubTestStateVal[DUPLICATE_DIMM_TEST_INDEX]); goto Finish; } } pResult->SubTestName[SYSTEMCAP_TEST_INDEX] = CatSPrint(NULL, L"System Capability"); ReturnCode = GetSystemCapabilitiesInfo(&gNvmDimmDriverNvmDimmConfig, &SysCapInfo); if ((pResult->SubTestStateVal[SYSTEMCAP_TEST_INDEX] & DIAG_STATE_MASK_ABORTED) != 0) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_CONFIG_ABORTED_INTERNAL_ERROR), EVENT_CODE_630, DIAG_STATE_MASK_ABORTED, &pResult->SubTestMessage[SYSTEMCAP_TEST_INDEX], &pResult->SubTestStateVal[SYSTEMCAP_TEST_INDEX]); goto Finish; } if (!SysCapInfo.AdrSupported) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_CONFIG_NO_ADR_SUPPORT), EVENT_CODE_629, DIAG_STATE_MASK_FAILED, &pResult->SubTestMessage[SYSTEMCAP_TEST_INDEX], &pResult->SubTestStateVal[SYSTEMCAP_TEST_INDEX]); } ReturnCode = CheckSystemSupportedCapabilities(ppDimms, DimmCount, DimmIdPreference, &pResult->SubTestMessage[SYSTEMCAP_TEST_INDEX], &pResult->SubTestStateVal[SYSTEMCAP_TEST_INDEX]); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("The check for System supported capabilities failed."); if ((pResult->SubTestStateVal[SYSTEMCAP_TEST_INDEX] & DIAG_STATE_MASK_ABORTED) != 0) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_CONFIG_ABORTED_INTERNAL_ERROR), EVENT_CODE_630, DIAG_STATE_MASK_ABORTED, &pResult->SubTestMessage[SYSTEMCAP_TEST_INDEX], &pResult->SubTestStateVal[SYSTEMCAP_TEST_INDEX]); goto Finish; } } pResult->SubTestName[NAMESPACE_LSA_TEST_INDEX] = CatSPrint(NULL, L"Namespace LSA"); ReturnCode = CheckNamespaceLabelAreaIndex(ppDimms, DimmCount, DimmIdPreference, &pResult->SubTestMessage[NAMESPACE_LSA_TEST_INDEX], &pResult->SubTestStateVal[NAMESPACE_LSA_TEST_INDEX]); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("The check for Namespace label retrieve failed."); if ((pResult->SubTestStateVal[NAMESPACE_LSA_TEST_INDEX] & DIAG_STATE_MASK_ABORTED) != 0) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_CONFIG_ABORTED_INTERNAL_ERROR), EVENT_CODE_630, DIAG_STATE_MASK_ABORTED, &pResult->SubTestMessage[NAMESPACE_LSA_TEST_INDEX], &pResult->SubTestStateVal[NAMESPACE_LSA_TEST_INDEX]); goto Finish; } } pResult->SubTestName[PCD_TEST_INDEX] = CatSPrint(NULL, L"PCD"); ReturnCode = CheckPlatformConfigurationData(ppDimms, DimmCount, DimmIdPreference, &pResult->SubTestMessage[PCD_TEST_INDEX], &pResult->SubTestStateVal[PCD_TEST_INDEX]); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("The check for platform configuration data failed."); if ((pResult->SubTestStateVal[PCD_TEST_INDEX] & DIAG_STATE_MASK_ABORTED) != 0) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_CONFIG_ABORTED_INTERNAL_ERROR), EVENT_CODE_630, DIAG_STATE_MASK_ABORTED, &pResult->SubTestMessage[PCD_TEST_INDEX], &pResult->SubTestStateVal[PCD_TEST_INDEX]); goto Finish; } } ReturnCode = EFI_SUCCESS; goto Finish; Finish: FREE_HII_POINTER(SysCapInfo.PtrInterleaveFormatsSupported); FREE_HII_POINTER(SysCapInfo.PtrInterleaveSize); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Diagnostics/ConfigDiagnostic.h000066400000000000000000000020651440615110200251600ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef CONFIG_DIAGNOSTICS_H_ #define CONFIG_DIAGNOSTICS_H_ #include "CoreDiagnostics.h" typedef struct _BROKEN_IS { UINT16 InterleaveSetIndex; DIMM_UNIQUE_IDENTIFIER MissingDimmIdentifier[MAX_DIMMS]; DIMM_LOCATION CurrentDimmLocations[MAX_DIMMS]; DIMM_LOCATION MisplacedDimmLocations[MAX_DIMMS]; UINT16 MissingDimmCount; } BROKEN_IS; /** Run platform configuration diagnostics for the list of DIMMs, and appropriately populate the result messages, and test-state. @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[out] pResult Pointer of structure with diagnostics test result @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS RunConfigDiagnostics( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT8 DimmIdPreference, OUT DIAG_INFO *pResult ); #endif ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Diagnostics/CoreDiagnostics.c000066400000000000000000000354261440615110200250300ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "CoreDiagnostics.h" #include "QuickDiagnostic.h" #include "ConfigDiagnostic.h" #include "SecurityDiagnostic.h" #include "FwDiagnostic.h" extern NVMDIMMDRIVER_DATA *gNvmDimmData; /** Append to the results string for a particular diagnostic test, and modify the test state as per the message being appended. @param[in] pStrToAppend Pointer to the message string to be appended @param[in] DiagStateMask State corresponding to the string that is being appended @param[in out] ppResult Pointer to the result string of the particular test-suite's messages @param[in out] pDiagState Pointer to the particular test state @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL **/ EFI_STATUS AppendToDiagnosticsResult ( IN DIMM *pDimm OPTIONAL, IN UINT32 Code OPTIONAL, IN CHAR16 *pStrToAppend, IN UINT8 DiagStateMask, IN OUT CHAR16 **ppResultStr, IN OUT UINT8 *pDiagState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pStrToAppend == NULL || ppResultStr == NULL || pDiagState == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *ppResultStr = CatSPrintClean(*ppResultStr, FORMAT_STR_NL, pStrToAppend); *pDiagState |= DiagStateMask; Finish: FREE_POOL_SAFE(pStrToAppend); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Helper function to convert the diagnostic test's result-state to its corresponding string form @param[in] DiagState The result-state of the test @retval NULL if the passed result-state was invalid @return String form of the diagnostic test's result-state **/ STATIC CHAR16 * GetDiagnosticState( IN UINT8 DiagState ) { NVDIMM_ENTRY(); CHAR16 *pDiagStateStr = NULL; if (DiagState & DIAG_STATE_MASK_ABORTED) { pDiagStateStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DIAGNOSTIC_STATE_ABORTED), NULL); } else if (DiagState & DIAG_STATE_MASK_FAILED) { pDiagStateStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DIAGNOSTIC_STATE_FAILED), NULL); } else if (DiagState & DIAG_STATE_MASK_WARNING) { pDiagStateStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DIAGNOSTIC_STATE_WARNING), NULL); } else if (DiagState & DIAG_STATE_MASK_OK) { pDiagStateStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DIAGNOSTIC_STATE_OK), NULL); } else if ((DiagState & DIAG_STATE_MASK_ALL) <= DIAG_STATE_MASK_OK) { pDiagStateStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DIAGNOSTIC_STATE_OK), NULL); } NVDIMM_EXIT(); return pDiagStateStr; } /** Helper function to retrieve the diagnostics test-name from the test index @param[in] DiagnosticTestIndex Diagnostic test index @retval NULL if the passed test index was invalid @return String form of the diagnostic test name **/ STATIC CHAR16 * GetDiagnosticTestName( IN UINT8 DiagnosticTestIndex ) { CHAR16 *pDiagTestStr = NULL; NVDIMM_ENTRY(); switch (DiagnosticTestIndex) { case QuickDiagnosticIndex: pDiagTestStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DIAGNOSTIC_QUICK_NAME), NULL); break; case ConfigDiagnosticIndex: pDiagTestStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DIAGNOSTIC_CONFIG_NAME), NULL); break; case SecurityDiagnosticIndex: pDiagTestStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DIAGNOSTIC_SECURITY_NAME), NULL); break; case FwDiagnosticIndex: pDiagTestStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DIAGNOSTIC_FW_NAME), NULL); break; default: NVDIMM_DBG("invalid diagnostic test"); break; } NVDIMM_EXIT(); return pDiagTestStr; } /** Add headers to the message results from all the tests that were run, and then append those messages into one single Diagnostics result string @param[in] pBuffer Array of the result messages for all tests @param[in] DiagState Array of the result state for all tests @param[out] ppResult Pointer to the final result string for all tests that were run @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL **/ EFI_STATUS CombineDiagnosticsTestResults( IN CHAR16 *pBuffer[], IN UINT8 DiagState[], OUT CHAR16 **ppResult ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINTN Index = 0; CHAR16 *pColonMarkStr = NULL; CHAR16 *pTestNameValueStr = NULL; CHAR16 *pDiagStateValueStr = NULL; CHAR16 *pTempHeaderStr = NULL; NVDIMM_ENTRY(); if (pBuffer == NULL || ppResult == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pColonMarkStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DCPMM_COLON_MARK), NULL); for (Index = 0; Index < DIAGNOSTIC_TEST_COUNT; Index++) { if (pBuffer[Index] != NULL) { //creating test name string pTestNameValueStr = GetDiagnosticTestName((UINT8)Index); if (pTestNameValueStr == NULL) { NVDIMM_DBG("Retrieval of the test name failed"); //log as warning state and a message continue; } //creating state string pDiagStateValueStr = GetDiagnosticState(DiagState[Index]); if (pDiagStateValueStr == NULL) { NVDIMM_DBG("Retrieval of the test state failed"); FREE_POOL_SAFE(pTestNameValueStr); //log as warning state and a message continue; } pTempHeaderStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DIAGNOSTIC_TEST_NAME_HEADER), NULL); *ppResult = CatSPrintClean(*ppResult, FORMAT_STR FORMAT_STR L" " FORMAT_STR_NL, pTempHeaderStr, pColonMarkStr, pTestNameValueStr); FREE_POOL_SAFE(pTempHeaderStr); FREE_POOL_SAFE(pTestNameValueStr); pTempHeaderStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DIAGNOSTIC_STATE_HEADER), NULL); *ppResult = CatSPrintClean(*ppResult, FORMAT_STR FORMAT_STR L" " FORMAT_STR_NL, pTempHeaderStr, pColonMarkStr, pDiagStateValueStr); FREE_POOL_SAFE(pTempHeaderStr); FREE_POOL_SAFE(pDiagStateValueStr); pTempHeaderStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_DIAGNOSTIC_MESSAGE_HEADER), NULL); *ppResult = CatSPrintClean(*ppResult, FORMAT_STR FORMAT_STR L"\n" FORMAT_STR_NL, pTempHeaderStr, pColonMarkStr, pBuffer[Index]); FREE_POOL_SAFE(pTempHeaderStr); FREE_POOL_SAFE(pTestNameValueStr); FREE_POOL_SAFE(pDiagStateValueStr); } } Finish: FREE_POOL_SAFE(pColonMarkStr); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** The fundamental core diagnostics function that is used by both the NvmDimmConfig protocol and the DriverDiagnostic protocols. It runs the specified diagnostics tests on the list of specified dimms, and returns a single combined test result message @param[in] ppDimms The platform DIMM pointers list @param[in] DimmsNum Platform DIMMs count @param[in] pDimmIds Pointer to an array of user-specified DIMM IDs @param[in] DimmIdsCount Number of items in the array of user-specified DIMM IDs @param[in] DiagnosticsTest The selected tests bitmask @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[out] ppResult Pointer to the structure with information about test @retval EFI_SUCCESS Test executed correctly @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS CoreStartDiagnostics( IN DIMM **ppDimms, IN UINT32 DimmsNum, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT8 DiagnosticsTest, IN UINT8 DimmIdPreference, OUT DIAG_INFO **ppResult ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TempReturnCode = EFI_SUCCESS; DIMM **ppSpecifiedDimms = NULL; UINT16 SpecifiedDimmsNum = 0; LIST_ENTRY *pDimmList = NULL; UINT32 PlatformDimmsCount = 0; DIMM *pCurrentDimm = NULL; UINTN Index = 0; DIAG_INFO *pBuffer = NULL; pBuffer = AllocateZeroPool(sizeof(*pBuffer)); NVDIMM_ENTRY(); if (ppDimms == NULL || ppResult == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (pBuffer == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *ppResult = pBuffer; if ((DimmIdPreference != DISPLAY_DIMM_ID_HANDLE) && (DimmIdPreference != DISPLAY_DIMM_ID_UID)) { NVDIMM_DBG("Invalid value for Dimm Id preference"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if ((DiagnosticsTest & DIAGNOSTIC_TEST_ALL) == 0) { NVDIMM_DBG("Invalid diagnostics test"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Populate the specified dimms for quick diagnostics if ((DiagnosticsTest & DIAGNOSTIC_TEST_QUICK) && (DimmIdsCount > 0)) { if (pDimmIds == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pDimmList = &gNvmDimmData->PMEMDev.Dimms; ReturnCode = GetListSize(pDimmList, &PlatformDimmsCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on DimmListSize"); goto Finish; } if (DimmIdsCount > PlatformDimmsCount) { NVDIMM_DBG("User specified Dimm count exceeds the platform Dimm count."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ppSpecifiedDimms = AllocateZeroPool(DimmIdsCount * sizeof(DIMM *)); if (ppSpecifiedDimms == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } for (Index = 0; Index < DimmIdsCount; Index++) { pCurrentDimm = GetDimmByPid(pDimmIds[Index], pDimmList); if (pCurrentDimm == NULL) { NVDIMM_DBG("Failed on GetDimmByPid. Does DIMM 0x%04x exist?", pDimmIds[Index]); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ppSpecifiedDimms[SpecifiedDimmsNum] = pCurrentDimm; SpecifiedDimmsNum++; } } if (DiagnosticsTest & DIAGNOSTIC_TEST_QUICK) { pBuffer->TestName = GetDiagnosticTestName(QuickDiagnosticIndex); if (SpecifiedDimmsNum > 0) { TempReturnCode = RunQuickDiagnostics(ppSpecifiedDimms, (UINT16)SpecifiedDimmsNum, DimmIdPreference, pBuffer); } else { TempReturnCode = RunQuickDiagnostics(ppDimms, (UINT16)DimmsNum, DimmIdPreference, pBuffer); } if (EFI_ERROR(TempReturnCode)) { KEEP_ERROR(ReturnCode, TempReturnCode); NVDIMM_DBG("Quick diagnostics failed. (" FORMAT_EFI_STATUS ")", TempReturnCode); } TempReturnCode = UpdateTestState(pBuffer, QuickDiagnosticIndex); if (EFI_ERROR(TempReturnCode)) { KEEP_ERROR(ReturnCode, TempReturnCode); NVDIMM_DBG("Quick diagnostics failed while updating state."); } } else if (DiagnosticsTest & DIAGNOSTIC_TEST_CONFIG) { pBuffer->TestName = GetDiagnosticTestName(ConfigDiagnosticIndex); TempReturnCode = RunConfigDiagnostics(ppDimms, (UINT16)DimmsNum, DimmIdPreference, pBuffer); if (EFI_ERROR(TempReturnCode)) { KEEP_ERROR(ReturnCode, TempReturnCode); NVDIMM_DBG("Platform configuration diagnostics failed. (" FORMAT_EFI_STATUS ")", TempReturnCode); } TempReturnCode = UpdateTestState(pBuffer, ConfigDiagnosticIndex); if (EFI_ERROR(TempReturnCode)) { KEEP_ERROR(ReturnCode, TempReturnCode); NVDIMM_DBG("Platform configuration diagnostics failed while updating state."); } } else if (DiagnosticsTest & DIAGNOSTIC_TEST_SECURITY) { pBuffer->TestName = GetDiagnosticTestName(SecurityDiagnosticIndex); TempReturnCode = RunSecurityDiagnostics(ppDimms, (UINT16)DimmsNum, DimmIdPreference, pBuffer); if (EFI_ERROR(TempReturnCode)) { KEEP_ERROR(ReturnCode, TempReturnCode); NVDIMM_DBG("Security diagnostics failed. (" FORMAT_EFI_STATUS ")", TempReturnCode); } TempReturnCode = UpdateTestState(pBuffer, SecurityDiagnosticIndex); if (EFI_ERROR(TempReturnCode)) { KEEP_ERROR(ReturnCode, TempReturnCode); NVDIMM_DBG("Securit diagnostics failed while updating state."); } } else if (DiagnosticsTest & DIAGNOSTIC_TEST_FW) { pBuffer->TestName = GetDiagnosticTestName(FwDiagnosticIndex); TempReturnCode = RunFwDiagnostics(ppDimms, (UINT16)DimmsNum, DimmIdPreference, pBuffer); if (EFI_ERROR(TempReturnCode)) { KEEP_ERROR(ReturnCode, TempReturnCode); NVDIMM_DBG("Firmware and consistency settings diagnostics failed. (" FORMAT_EFI_STATUS ")", TempReturnCode); } TempReturnCode = UpdateTestState(pBuffer, FwDiagnosticIndex); if (EFI_ERROR(TempReturnCode)) { KEEP_ERROR(ReturnCode, TempReturnCode); NVDIMM_DBG("Firmware and consistency settings diagnostics failed while updating state."); } } else { NVDIMM_DBG("Invalid Diagnostic Test Id"); ReturnCode = EFI_INVALID_PARAMETER; } Finish: FREE_POOL_SAFE(ppSpecifiedDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** This function should be used to update status of the test based on information stored inside diagnostic information structure. @param[in] pBuffer Pointer to Diagnostic information structure @param[in] DiagnosticTestIndex Test Index @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS UpdateTestState( IN DIAG_INFO *pBuffer, IN UINT8 DiagnosticTestIndex ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINTN Id = 0; BOOLEAN IsTestPassed = TRUE; if (pBuffer != NULL) { for (Id = 0; Id < MAX_NO_OF_DIAGNOSTIC_SUBTESTS; Id++) { if (pBuffer->SubTestName[Id] != NULL) { pBuffer->SubTestState[Id] = GetDiagnosticState(pBuffer->SubTestStateVal[Id]); pBuffer->StateVal |= (pBuffer->SubTestStateVal[Id] & DIAG_STATE_MASK_ALL); } } if ((pBuffer->StateVal & DIAG_STATE_MASK_ALL) > DIAG_STATE_MASK_OK) { IsTestPassed = FALSE; } pBuffer->State = GetDiagnosticState(pBuffer->StateVal); if (pBuffer->Message == NULL) { if (IsTestPassed == TRUE) { switch (DiagnosticTestIndex) { case QuickDiagnosticIndex: APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_QUICK_SUCCESS), EVENT_CODE_500, DIAG_STATE_MASK_OK, &pBuffer->Message, &pBuffer->StateVal); break; case ConfigDiagnosticIndex: APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_CONFIG_SUCCESS), EVENT_CODE_600, DIAG_STATE_MASK_OK, &pBuffer->Message, &pBuffer->StateVal); break; case SecurityDiagnosticIndex: APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_SECURITY_SUCCESS), EVENT_CODE_800, DIAG_STATE_MASK_OK, &pBuffer->Message, &pBuffer->StateVal); break; case FwDiagnosticIndex: APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_FW_SUCCESS), EVENT_CODE_900, DIAG_STATE_MASK_OK, &pBuffer->Message, &pBuffer->StateVal); break; default: NVDIMM_DBG("invalid diagnostic test"); break; } } } } else { NVDIMM_DBG("invalid parameter to set state"); ReturnCode = EFI_INVALID_PARAMETER; } return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Diagnostics/CoreDiagnostics.h000066400000000000000000000163601440615110200250310ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _CORE_DIAGNOSTICS_H_ #define _CORE_DIAGNOSTICS_H_ #include "Uefi.h" #include "Dimm.h" #include "NvmTypes.h" #include #include "NvmDimmDriver.h" #include #include #include "FwUtility.h" #include "Utility.h" #include "NvmSecurity.h" #include #include #include #include #include "NvmDimmDriverData.h" #include #include #include #define APPEND_RESULT_TO_THE_LOG(pDimm,String,Code,StateMask,ppResult,pState,...) { \ CHAR16 *pTempHiiString = HiiGetString(gNvmDimmData->HiiHandle, String, NULL); \ CHAR16 *pTempHiiString1 = CatSPrintClean(NULL, pTempHiiString, ## __VA_ARGS__); \ FREE_POOL_SAFE(pTempHiiString); \ if (pTempHiiString1) \ AppendToDiagnosticsResult(pDimm, Code, pTempHiiString1, StateMask, ppResult, pState); \ } /** Diagnostics State bitmasks **/ #define DIAG_STATE_MASK_OK BIT0 #define DIAG_STATE_MASK_WARNING BIT1 #define DIAG_STATE_MASK_FAILED BIT2 #define DIAG_STATE_MASK_ABORTED BIT3 #define DIAG_STATE_MASK_ALL (DIAG_STATE_MASK_OK | DIAG_STATE_MASK_WARNING | DIAG_STATE_MASK_FAILED | DIAG_STATE_MASK_ABORTED) /** Event Codes' definitions **/ /* Diagnostic Quick Eve nts **/ #define EVENT_CODE_500 500 #define EVENT_CODE_501 501 #define EVENT_CODE_502 502 #define EVENT_CODE_503 503 #define EVENT_CODE_504 504 #define EVENT_CODE_505 505 #define EVENT_CODE_506 506 #define EVENT_CODE_507 507 #define EVENT_CODE_511 511 #define EVENT_CODE_513 513 #define EVENT_CODE_514 514 #define EVENT_CODE_515 515 #define EVENT_CODE_519 519 #define EVENT_CODE_520 520 #define EVENT_CODE_521 521 #define EVENT_CODE_522 522 #define EVENT_CODE_523 523 #define EVENT_CODE_529 529 #define EVENT_CODE_530 530 #define EVENT_CODE_533 533 #define EVENT_CODE_534 534 #define EVENT_CODE_535 535 #define EVENT_CODE_536 536 #define EVENT_CODE_537 537 #define EVENT_CODE_538 538 #define EVENT_CODE_539 539 #define EVENT_CODE_540 540 #define EVENT_CODE_541 541 #define EVENT_CODE_542 542 #define EVENT_CODE_543 543 #define EVENT_CODE_544 544 #define EVENT_CODE_545 545 /* Diagnostic Config Platform Events **/ #define EVENT_CODE_600 600 #define EVENT_CODE_601 601 #define EVENT_CODE_606 606 #define EVENT_CODE_608 608 #define EVENT_CODE_609 609 #define EVENT_CODE_618 618 #define EVENT_CODE_621 621 #define EVENT_CODE_622 622 #define EVENT_CODE_623 623 #define EVENT_CODE_624 624 #define EVENT_CODE_625 625 #define EVENT_CODE_626 626 #define EVENT_CODE_627 627 #define EVENT_CODE_628 628 #define EVENT_CODE_629 629 #define EVENT_CODE_630 630 #define EVENT_CODE_631 631 #define EVENT_CODE_632 632 #define EVENT_CODE_633 633 /* Diagnostic Security Events **/ #define EVENT_CODE_800 800 #define EVENT_CODE_801 801 #define EVENT_CODE_802 802 #define EVENT_CODE_804 804 #define EVENT_CODE_805 805 /* Diagnostic Firmware Events **/ #define EVENT_CODE_900 900 #define EVENT_CODE_901 901 #define EVENT_CODE_902 902 #define EVENT_CODE_903 903 #define EVENT_CODE_904 904 #define EVENT_CODE_905 905 #define EVENT_CODE_906 906 #define EVENT_CODE_910 910 #define EVENT_CODE_911 911 typedef enum { QuickDiagnosticIndex, ConfigDiagnosticIndex, SecurityDiagnosticIndex, FwDiagnosticIndex } DiagnosticTestIndex; /** The fundamental core diagnostics function that is used by both the NvmDimmConfig protocol and the DriverDiagnostic protocols. It runs the specified diagnostics tests on the list of specified dimms, and returns a single combined test result message @param[in] ppDimms The platform DIMM pointers list @param[in] DimmsNum Platform DIMMs count @param[in] pDimmIds Pointer to an array of user-specified DIMM IDs @param[in] DimmIdsCount Number of items in the array of user-specified DIMM IDs @param[in] DiagnosticsTest The selected tests bitmask @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[out] ppResult Pointer to the structure with information about test result @retval EFI_SUCCESS Test executed correctly @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS CoreStartDiagnostics( IN DIMM **ppDimms, IN UINT32 DimmsNum, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT8 DiagnosticsTest, IN UINT8 DimmIdPreference, OUT DIAG_INFO **ppResult ); /** Function to create a string, using the EFI_STRING_ID and the variable number of arguments for the format string Storage for the formatted Unicode string returned is allocated using AllocatePool(). The pointer to the created string is returned. The caller is responsible for freeing the returned string. @param[in] StringId ID of string @param[in] NumOfArgs Number of arguments passed @param[in] ... The variable argument list @retval NULL There was not enough available memory. @return Null-terminated formatted Unicode string. **/ CHAR16 * CreateDiagnosticStr ( IN EFI_STRING_ID StringId, IN UINT16 NumOfArgs, ... ); /** Append to the results string for a particular diagnostic test, and modify the test state as per the message being appended. @param[in] pStrToAppend Pointer to the message string to be appended @param[in] DiagStateMask State corresponding to the string that is being appended @param[in out] ppResult Pointer to the result string of the particular test-suite's messages @param[in out] pDiagState Pointer to the particular test state @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL **/ EFI_STATUS AppendToDiagnosticsResult ( IN DIMM *pDimm OPTIONAL, IN UINT32 Code OPTIONAL, IN CHAR16 *pStrToAppend, IN UINT8 DiagStateMask, IN OUT CHAR16 **ppResultStr, IN OUT UINT8 *pDiagState ); /** Add headers to the message results from all the tests that were run, and then append those messages into one single Diagnostics result string @param[in] pBuffer Array of the result messages for all tests @param[in] DiagState Array of the result state for all tests @param[out] ppResult Pointer to the final result string for all tests that were run @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL **/ EFI_STATUS CombineDiagnosticsTestResults( IN CHAR16 *pBuffer[], IN UINT8 DiagState[], OUT CHAR16 **ppResult ); /** This function should be used to update status of the test based on information stored inside diagnostic information structure. @param[in] pBuffer Pointer to Diagnostic information structure @param[in] DiagnosticTestIndex Test Index @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS UpdateTestState( IN DIAG_INFO *pBuffer, IN UINT8 DiagnosticTestIndex ); #endif ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Diagnostics/DiagnosticsMessages.uni000066400000000000000000000357521440615110200262620ustar00rootroot00000000000000//// // Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause //// #langdef en "English" //General #string STR_DIAGNOSTIC_TEST_NAME_HEADER #language en "TestName" #string STR_DIAGNOSTIC_STATE_HEADER #language en "State" #string STR_DIAGNOSTIC_MESSAGE_HEADER #language en "Message" #string STR_DIAGNOSTIC_QUICK_NAME #language en "Quick" #string STR_DIAGNOSTIC_CONFIG_NAME #language en "Config" #string STR_DIAGNOSTIC_SECURITY_NAME #language en "Security" #string STR_DIAGNOSTIC_FW_NAME #language en "FW" #string STR_DIAGNOSTIC_STATE_OK #language en "Ok" #string STR_DIAGNOSTIC_STATE_WARNING #language en "Warning" #string STR_DIAGNOSTIC_STATE_FAILED #language en "Failed" #string STR_DIAGNOSTIC_STATE_ABORTED #language en "Aborted" #string STR_DIAGNOSTIC_NOT_AVAILABLE #language en "N/A" #string STR_DIAGNOSTIC_LOWER #language en "lower" #string STR_DIAGNOSTIC_GREATER #language en "greater" //Config Changes #string STR_CONFIG_CHANGE_NEW_GOAL #language en "A new configuration goal has been saved on PMem module 0x%04x." #string STR_CONFIG_CHANGE_DELETE_GOAL #language en "A configuration goal has been deleted from PMem module 0x%04x." #string STR_CONFIG_SENSOR_SET_CHANGED #language en "The %ls sensor settings have been changed on PMem module 0x%04x." //Quick #string STR_QUICK_SUCCESS #language en "The quick health check succeeded." #string STR_QUICK_UNMANAGEABLE_DIMM_SUBSYSTEM_VENDOR_ID #language en "The quick health check detected that PMem module %ls is not manageable because subsystem vendor ID 0x%04x is not supported." #string STR_QUICK_UNMANAGEABLE_DIMM_SUBSYSTEM_DEVICE_ID #language en "The quick health check detected that PMem module %ls is not manageable because subsystem device ID 0x%04x is not supported." #string STR_QUICK_UNMANAGEABLE_DIMM_FW_API_VERSION #language en "The quick health check detected that PMem module %ls is not manageable because firmware API version %ls is not supported." #string STR_QUICK_BAD_HEALTH_STATE #language en "The quick health check detected that PMem module %ls is reporting a bad health state %ls." #string STR_QUICK_MEDIA_TEMP_EXCEEDS_ALARM_THR #language en "The quick health check detected that PMem module %ls is reporting a media temperature of %d C which is above the alarm threshold %d C." #string STR_QUICK_SPARE_CAPACITY_BELOW_ALARM_THR #language en "The quick health check detected that PMem module %ls is reporting percentage remaining at %d%% which is less than the alarm threshold %d%%." #string STR_QUICK_CONTROLLER_TEMP_EXCEEDS_ALARM_THR #language en "The quick health check detected that PMem module %ls is reporting a controller temperature of %d C which is above the alarm threshold %d C." #string STR_QUICK_BSR_NOT_READABLE #language en "The quick health check detected that the boot status register of PMem module %ls is not readable." #string STR_QUICK_BSR_MEDIA_NOT_READY #language en "The quick health check detected that the firmware on PMem module %ls is reporting that the media is not ready." #string STR_QUICK_BSR_MEDIA_ERROR #language en "The quick health check detected that the firmware on PMem module %ls is reporting an error in the media." #string STR_QUICK_BSR_BIOS_POST_TRAINING_FAILED #language en "The quick health check detected that PMem module %ls failed to initialize BIOS POST testing." #string STR_QUICK_BSR_FW_NOT_INITIALIZED #language en "The quick health check detected that the firmware on PMem module %ls has not initialized successfully, last known Major : Minor Checkpoint is 0x%x:0x%x." #string STR_QUICK_BSR_MEDIA_ENGINE_STALLED #language en "The quick health check detected that the firmware on PMem module %ls has stalled the media interface engine." #string STR_QUICK_VIRAL_STATE #language en "The quick health check detected that PMem module %ls is reporting a viral state. The PMem module is now read - only." #string STR_QUICK_NO_PACKAGE_SPARES_AVAILABLE #language en "The quick health check detected that PMem module %ls is reporting that it has no package spares available." #string STR_QUICK_DIRTY_SHUTDOWN #language en "The quick health check detected that the firmware on PMem module 0x%04x experienced a dirty shutdown before its latest restart." #string STR_QUICK_AIT_DRAM_NOT_READY #language en "The quick health check detected that the firmware on PMem module %ls is reporting that the AIT Dram is not ready." #string STR_QUICK_BSR_MEDIA_DISABLED #language en "The quick health check detected that the firmware on PMem module %ls is reporting that the media is disabled." #string STR_QUICK_AIT_DISABLED #language en "The quick health check detected that the firmware on PMem module %ls is reporting that the AIT Dram is disabled." #string STR_QUICK_FW_LOAD_FAILED #language en "The quick health check detected that the firmware on PMem module %ls failed to load successfully." #string STR_QUICK_BSR_CPU_EXCEPTION #language en "The quick health check detected that the firmware on PMem module %ls has an internal CPU error, last known Major:Minor Checkpoint is 0x%x:0x%x." #string STR_QUICK_BSR_DDRT_IO_NOT_COMPLETE #language en "PMem module %ls is reporting that the DDRT IO Init is not complete." #string STR_QUICK_BSR_DDRT_IO_NOT_STARTED #language en "PMem module %ls is reporting that the DDRT IO Init did not start." #string STR_QUICK_BSR_MAILBOX_NOT_READY #language en "PMem module %ls is reporting that the mailbox interface is not ready." #string STR_QUICK_DDRT_TRAINING_NOT_COMPLETE_FAILED #language en "PMem module %ls is reporting that DDRT Training is not complete/failed." #string STR_QUICK_ABORTED_DIMM_INTERNAL_ERROR #language en "An internal error caused the quick health check to abort on PMem module %ls." #string STR_QUICK_BSR_REBOOT_REQUIRED #language en "The quick health check detected that PMem module %ls is reporting reboot required." #string STR_QUICK_FW_BUSY #language en "The quick health check detected that PMem module 0x%04x is busy." #string STR_QUICK_ACPI_NVDIMM_SPA_NOT_MAPPED #language en "The quick health check detected that the platform FW did not map a region to SPA on PMem module %ls. ACPI NFIT NVDIMM State Flags Error Bit 6 Set" #string STR_QUICK_BSR_ROM_FAILED_INITIALIZATION #language en "The quick health check detected that the ROM on PMem module %ls has failed to complete initialization, last known Major : Minor Checkpoint is 0x%x:0x%x." //Platform Config #string STR_CONFIG_STATUS_EXCEEDS_PARTITION_SIZE #language en "EXCEEDS_PARTITION_SIZE" #string STR_CONFIG_STATUS_UNSUPPORTED_ALIGNMENT #language en "UNSUPPORTED_PARTITION_ALIGNMENT" #string STR_CONFIG_STATUS_PMEM_MODULE_MISSING_IN_ISET #language en "PMEM_MODULE_MISSING_IN_INTERLEAVE_SET" #string STR_CONFIG_STATUS_MATCHING_ISET_NOT_FOUND #language en "MATCHING_INTERLEAVE_SET_NOT_FOUND" #string STR_CONFIG_STATUS_PMEM_MODULE_FIRMWARE_ERROR #language en "PMEM_MODULE_FIRMWARE_ERROR" #string STR_CONFIG_STATUS_INSUFFICIENT_SILICON_RESOURCES #language en "INSUFFICIENT_SILICON_RESOURCES" #string STR_CONFIG_STATUS_INSUFFICIENT_SPA_SPACE #language en "INSUFFICIENT_SPA_SPACE" #string STR_CONFIG_STATUS_CIN_MISSING #language en "CIN_MISSING_IN_ONE_OR_MORE_PMEM_MODULES" #string STR_CONFIG_STATUS_CHANNEL_NOT_MATCH #language en "CHANNEL_INTERLEAVE_ACROSS_IMCS_NOT_MATCH" #string STR_CONFIG_STATUS_REQUEST_UNSUPPORTED #language en "REQUEST_UNSUPPORTED" #string STR_CONFIG_STATUS_CPU_MAX_MEMORY_LIMIT_VIOLATION #language en "CPU_MAX_MEMORY_LIMIT_VIOLATION" #string STR_CONFIG_STATUS_NM_FM_RATIO_UNSUPPORTED #language en "NM_FM_RATIO_UNSUPPORTED" #string STR_CONFIG_STATUS_POPULATION_VIOLATION #language en "POPULATION_VIOLATION" #string STR_CONFIG_STATUS_POPULATION_VIOLATION_BUT_PM_MAPPED #language en "POPULATION_VIOLATION_BUT_PM_MAPPED" #string STR_CONFIG_STATUS_UNKNOWN #language en "UNKNOWN" #string STR_CONFIG_SUCCESS #language en "The platform configuration check succeeded." #string STR_CONFIG_NO_MANAGEABLE_DIMMS #language en "The platform configuration check detected that there are no manageable PMem modules." #string STR_CONFIG_DIMM_NOT_CONFIGURED #language en "The platform configuration check detected that PMem module %ls is not configured." #string STR_CONFIG_DUPLICATE_DIMM_UID #language en "The platform configuration check detected %d PMem modules installed on the platform with the same serial number %ls." #string STR_CONFIG_GOAL_NOT_APPLIED #language en "The platform configuration check detected that PMem module %ls has a goal configuration that has not yet been applied. A system reboot is required for the new configuration to take effect." #string STR_CONFIG_DIMM_FAILED_TO_INITIALIZE #language en "The platform configuration check detected that PMem module with physical ID %ls is present in the system but failed to initialize." #string STR_CONFIG_INVALID_PCD_DATA #language en "The platform configuration check detected PCD contains invalid data on PMem module %ls." #string STR_CONFIG_UNABLE_TO_READ_NS_INFO #language en "The platform configuration check was unable to retrieve the namespace information." #string STR_CONFIG_NO_OS_PROVISIONING #language en "The platform configuration check detected that the BIOS settings do not currently allow memory provisioning from this software." #string STR_CONFIG_GOAL_FAILED_DATA #language en "The platform configuration check detected that the BIOS could not apply the configuration goal on PMem module %ls because of errors in the goal data. The detailed status is %ls." #string STR_CONFIG_GOAL_FAILED_INSUFFICIENT_RESOURCES #language en "The platform configuration check detected that the BIOS could not apply the configuration goal on PMem module %ls because the system has insufficient resources. The detailed status is %ls." #string STR_CONFIG_GOAL_FAILED_FIRMWARE #language en "The platform configuration check detected that the BIOS could not apply the configuration goal on PMem module %ls because of a firmware error. The detailed status is %ls." #string STR_CURRENT_CONFIG_FAILED_DATA #language en "The platform configuration check detected that the BIOS could not fully map memory on PMem module %ls because of an error in current configuration. The detailed status is %ls." #string STR_CONFIG_GOAL_FAILED_UNKNOWN #language en "The platform configuration check detected that the BIOS could not apply the configuration goal on PMem module %ls for an unknown reason. The detailed status is %ls." #string STR_CONFIG_IS_BROKEN_DIMMS_MISSING #language en "The platform configuration check detected that interleave set %d is broken because the PMem modules were moved %ls." #string STR_CONFIG_NO_ADR_SUPPORT #language en "The platform configuration check detected that the platform does not support ADR and therefore data integrity is not guaranteed on the PMem modules." #string STR_CONFIG_ABORTED_INTERNAL_ERROR #language en "An internal error caused the platform configuration check to abort." #string STR_CONFIG_DIAG_COUT_CONFIG_DETAILED_STATUS #language en "COUT table status: (%d [%ls]), Partition change table status: (%ls), Interleave change table 1 status: (%ls), Interleave change table 2 status: (%ls)" #string STR_CONFIG_DIAG_CCUR_CONFIG_DETAILED_STATUS #language en "CCUR table status: (%d [%ls])" #string STR_CONFIG_IS_BROKEN_DIMMS_MISSING_LOCATION #language en "The platform configuration check detected that interleave set %d is broken because the PMem module with UID: %ls is missing from location (Socket-Die-iMC-Channel-Slot) %d-%d-%d-%d-%d." #string STR_CONFIG_IS_BROKEN_DIMMS_MISPLACED_LOCATION #language en "The platform configuration check detected that interleave set %d is broken because the PMem module with UID: %ls is misplaced. It is currently in location (Socket-Die-iMC-Channel-Slot) %d-%d-%d-%d-%d and should be moved to %d-%d-%d-%d-%d." //Security #string STR_SECURITY_SUCCESS #language en "The security check succeeded." #string STR_SECURITY_NO_MANAGEABLE_DIMMS #language en "The security check detected that there are no manageable PMem modules." #string STR_SECURITY_INCONSISTENT #language en "The security check detected that security settings are inconsistent %ls." #string STR_SECURITY_NOT_SUPPORTED #language en "The security check detected that security is not supported on all PMem modules." #string STR_SECURITY_ABORTED_INTERNAL_ERROR #language en "An internal error caused the security check to abort." //Fw #string STR_FW_SUCCESS #language en "The firmware consistency and settings check succeeded." #string STR_FW_NO_MANAGEABLE_DIMMS #language en "The firmware consistency and settings check detected that there are no manageable PMem modules." #string STR_FW_INCONSISTENT #language en "The firmware consistency and settings check detected that firmware version on PMem module %ls with subsystem device ID 0x%04x is nonoptimal, preferred version is %ls." #string STR_FW_MEDIA_TEMPERATURE_THRESHOLD_ERROR #language en "The firmware consistency and settings check detected that PMem module 0x%04x has a non-critical media temperature threshold of %d C which is above the fatal threshold %d C." #string STR_FW_CONTROLLER_TEMPERATURE_THRESHOLD_ERROR #language en "The firmware consistency and settings check detected that PMem module 0x%04x has a non-critical controller temperature threshold of %d C which is above the fatal threshold %d C." #string STR_FW_SPARE_BLOCK_THRESHOLD_ERROR #language en "The firmware consistency and settings check detected that PMem module 0x%04x is reporting a percentage remaining of %d%% which is below the recommended threshold %d%%" #string STR_FW_LOG_LEVEL_ERROR #language en "The firmware consistency and settings check detected that PMem module 0x%04x is reporting a firmware log level of %d, default log level is %d." #string STR_FW_ABORTED_INTERNAL_ERROR #language en "An internal error caused the firmware consistency and settings check to abort." #string STR_FW_INCONSISTENT_VIRAL_POLICY #language en "The firmware consistency and settings check detected that PMem modules have inconsistent viral policy settings."ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Diagnostics/FwDiagnostic.c000066400000000000000000000461661440615110200243340ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifdef OS_BUILD #include #endif // OS_BUILD #include "FwDiagnostic.h" extern NVMDIMMDRIVER_DATA *gNvmDimmData; #define DEFAULT_FW_LOG_LEVEL_VALUE FW_LOG_LEVEL_ERROR #define FW_CONSIST_TEST_INDEX 0 #define VIRAL_POLICY_CONSIST_TEST_INDEX 1 #define THRESHOLD_TEST_INDEX 2 #define SYS_TIME_TEST_INDEX 3 /** Run Fw diagnostics for the list of DIMMs, and appropriately populate the result in diagnostic structure. @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[out] pResult Pointer of structure with diagnostics test result @retval EFI_SUCCESS Test executed correctly @retval EFI_DEVICE_ERROR Test wasn't executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. @retval EFI_OUT_OF_RESOURCES when memory allocation fails. **/ EFI_STATUS RunFwDiagnostics( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT8 DimmIdPreference, OUT DIAG_INFO *pResult ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT16 Index = 0; NVDIMM_ENTRY(); if (pResult == NULL || DimmCount > MAX_DIMMS) { NVDIMM_DBG("The firmware consistency and settings diagnostics test aborted due to an internal error."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (DimmCount == 0 || ppDimms == NULL || GetManageableDimmsCount(ppDimms, DimmCount) == 0) { ReturnCode = EFI_SUCCESS; APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_FW_NO_MANAGEABLE_DIMMS), EVENT_CODE_901, DIAG_STATE_MASK_OK, &pResult->Message, &pResult->StateVal); goto Finish; } pResult->SubTestName[FW_CONSIST_TEST_INDEX] = CatSPrint(NULL, L"FW Consistency"); ReturnCode = CheckFwConsistency(ppDimms, DimmCount, DimmIdPreference, &pResult->SubTestMessage[FW_CONSIST_TEST_INDEX], &pResult->SubTestStateVal[FW_CONSIST_TEST_INDEX]); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("The check for firmware consistency failed."); if ((pResult->SubTestStateVal[FW_CONSIST_TEST_INDEX] & DIAG_STATE_MASK_ABORTED) != 0) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_FW_ABORTED_INTERNAL_ERROR), EVENT_CODE_910, DIAG_STATE_MASK_ABORTED, &pResult->SubTestMessage[FW_CONSIST_TEST_INDEX], &pResult->SubTestStateVal[FW_CONSIST_TEST_INDEX]); goto Finish; } } pResult->SubTestName[VIRAL_POLICY_CONSIST_TEST_INDEX] = CatSPrint(NULL, L"Viral Policy"); ReturnCode = CheckViralPolicyConsistency(ppDimms, DimmCount, &pResult->SubTestMessage[VIRAL_POLICY_CONSIST_TEST_INDEX], &pResult->SubTestStateVal[VIRAL_POLICY_CONSIST_TEST_INDEX]); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("The check for viral policy settings consistency failed"); if ((pResult->SubTestStateVal[VIRAL_POLICY_CONSIST_TEST_INDEX] & DIAG_STATE_MASK_ABORTED) != 0) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_FW_ABORTED_INTERNAL_ERROR), EVENT_CODE_910, DIAG_STATE_MASK_ABORTED, &pResult->SubTestMessage[VIRAL_POLICY_CONSIST_TEST_INDEX], &pResult->SubTestStateVal[VIRAL_POLICY_CONSIST_TEST_INDEX]); goto Finish; } } pResult->SubTestName[THRESHOLD_TEST_INDEX] = CatSPrint(NULL, L"Threshold check"); pResult->SubTestName[SYS_TIME_TEST_INDEX] = CatSPrint(NULL, L"System Time"); for (Index = 0; Index < DimmCount; Index++) { if (ppDimms[Index] == NULL) { ReturnCode = EFI_INVALID_PARAMETER; pResult->SubTestStateVal[THRESHOLD_TEST_INDEX] |= DIAG_STATE_MASK_ABORTED; goto Finish; } if (!IsDimmManageable(ppDimms[Index])) { continue; } ReturnCode = ThresholdsCheck(ppDimms[Index], &pResult->SubTestMessage[THRESHOLD_TEST_INDEX], &pResult->SubTestStateVal[THRESHOLD_TEST_INDEX]); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("The check for firmware threshold settings failed. Dimm handle 0x%04x.", ppDimms[Index]->DeviceHandle.AsUint32); if ((pResult->SubTestStateVal[THRESHOLD_TEST_INDEX] & DIAG_STATE_MASK_ABORTED) != 0) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_FW_ABORTED_INTERNAL_ERROR), EVENT_CODE_910, DIAG_STATE_MASK_ABORTED, &pResult->SubTestMessage[THRESHOLD_TEST_INDEX], &pResult->SubTestStateVal[THRESHOLD_TEST_INDEX]); goto Finish; } } } ReturnCode = EFI_SUCCESS; goto Finish; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Populate the list of unique subsystem device IDs across all the specified DIMMs @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[out] SubsystemDeviceIdList Array of the unique subsystem device IDs @param[out] pSubsystemDeviceIdListCount Pointer to the count of unique subsystem device IDs @param[out] pDiagState Pointer to the fw diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS PopulateSubsystemDeviceIdList( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, OUT UINT16 SubsystemDeviceIdList[], OUT UINT16 *pSubsystemDeviceIdListCount, OUT UINT8 *pDiagState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT16 TmpSubsystemDeviceIdListCount = 0; BOOLEAN DeviceIdExists = FALSE; UINTN Index = 0; UINTN Index1 = 0; NVDIMM_ENTRY(); if (ppDimms == NULL || SubsystemDeviceIdList == NULL || pSubsystemDeviceIdListCount == NULL || pDiagState == NULL) { ReturnCode = EFI_INVALID_PARAMETER; if (pDiagState != NULL) { *pDiagState |= DIAG_STATE_MASK_ABORTED; } goto Finish; } for (Index = 0; Index < DimmCount; Index++) { if (ppDimms[Index] == NULL) { ReturnCode = EFI_INVALID_PARAMETER; *pDiagState |= DIAG_STATE_MASK_ABORTED; goto Finish; } for (Index1 = 0; Index1 < TmpSubsystemDeviceIdListCount; Index1++) { if (SubsystemDeviceIdList[Index1] == ppDimms[Index]->SubsystemDeviceId) { DeviceIdExists = TRUE; break; } DeviceIdExists = FALSE; } if (!DeviceIdExists) { SubsystemDeviceIdList[TmpSubsystemDeviceIdListCount] = ppDimms[Index]->SubsystemDeviceId; TmpSubsystemDeviceIdListCount++; } } *pSubsystemDeviceIdListCount = TmpSubsystemDeviceIdListCount; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Determines the optimum firmware version for the specified list of DIMMs, for a particular subsystem device ID. @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[in] SubsystemDeviceId Specified subsystem device ID, to select the DIMMs for which to determine the optimum firmware version @param[out] pOptimumFwVer Pointer to the optimum firmware version @param[out] pDiagState Pointer to the fw diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. @retval EFI_DEVICE_ERROR Test wasn't executed correctly **/ EFI_STATUS GetOptimumFwVersion( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT16 SubsystemDeviceId, OUT FIRMWARE_VERSION *pOptimumFwVer, OUT UINT8 *pDiagState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; FIRMWARE_VERSION InitialFwVer; UINT16 Index = 0; UINT16 DimmIndex = 0; NVDIMM_ENTRY(); ZeroMem(&InitialFwVer, sizeof(InitialFwVer)); if (ppDimms == NULL || DimmCount == 0 || pOptimumFwVer == NULL || pDiagState == NULL) { ReturnCode = EFI_INVALID_PARAMETER; if (pDiagState != NULL) { *pDiagState |= DIAG_STATE_MASK_ABORTED; } goto Finish; } for (Index = 0; Index < DimmCount; Index++) { if (ppDimms[Index] == NULL) { ReturnCode = EFI_INVALID_PARAMETER; *pDiagState |= DIAG_STATE_MASK_ABORTED; goto Finish; } if (ppDimms[Index]->SubsystemDeviceId == SubsystemDeviceId) { if (InitialFwVer.FwProduct < ppDimms[Index]->FwVer.FwProduct) { InitialFwVer = ppDimms[Index]->FwVer; DimmIndex = Index; } else if (InitialFwVer.FwProduct == ppDimms[Index]->FwVer.FwProduct) { if (InitialFwVer.FwRevision < ppDimms[Index]->FwVer.FwRevision) { InitialFwVer = ppDimms[Index]->FwVer; DimmIndex = Index; } else if (InitialFwVer.FwRevision == ppDimms[Index]->FwVer.FwRevision) { if (InitialFwVer.FwSecurityVersion < ppDimms[Index]->FwVer.FwSecurityVersion) { InitialFwVer = ppDimms[Index]->FwVer; DimmIndex = Index; } else if (InitialFwVer.FwSecurityVersion == ppDimms[Index]->FwVer.FwSecurityVersion) { if (InitialFwVer.FwBuild < ppDimms[Index]->FwVer.FwBuild) { InitialFwVer = ppDimms[Index]->FwVer; DimmIndex = Index; } } } } } } if (InitialFwVer.FwProduct != 0 || InitialFwVer.FwRevision != 0 || InitialFwVer.FwSecurityVersion != 0 || InitialFwVer.FwBuild != 0) { *pOptimumFwVer = ppDimms[DimmIndex]->FwVer; } else { ReturnCode = EFI_DEVICE_ERROR; goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check firmware consistency for the specified DIMMs, and accordingly append to the fw diagnostics result. Also, accordingly modifies the test-state. @param[in] pDimm Pointer to the DIMM @param[in] pDimmStr Dimm string to be used in result messages @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[in out] ppResult Pointer to the result string of fw diagnostics message @param[out] pDiagState Pointer to the fw diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL @retval EFI_OUT_OF_RESOURCES when memory allocation fails. **/ EFI_STATUS CheckFwConsistency( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT8 DimmIdPreference, IN OUT CHAR16 **ppResultStr, OUT UINT8 *pDiagState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT16 SubsystemDeviceIdListCount = 0; UINT16 SubsystemDeviceIdList[MAX_DIMMS]; FIRMWARE_VERSION OptimumFwVersion[MAX_DIMMS]; CHAR16 DimmUid[MAX_DIMM_UID_LENGTH]; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; CHAR16 *pAppendedDimmsStr = NULL; CHAR16 OptimumFwVersionStr[FW_VERSION_LEN]; CHAR16 TmpFwVerStr[FW_VERSION_LEN]; UINTN Index = 0; UINTN Index1 = 0; NVDIMM_ENTRY(); ZeroMem(SubsystemDeviceIdList, sizeof(SubsystemDeviceIdList)); ZeroMem(OptimumFwVersion, sizeof(OptimumFwVersion)); ZeroMem(OptimumFwVersionStr, sizeof(OptimumFwVersionStr)); ZeroMem(TmpFwVerStr, sizeof(TmpFwVerStr)); if (DimmCount == 0 || ppDimms == NULL || DimmCount > MAX_DIMMS || ppResultStr == NULL || pDiagState == NULL) { ReturnCode = EFI_INVALID_PARAMETER; if (pDiagState != NULL) { *pDiagState |= DIAG_STATE_MASK_ABORTED; } goto Finish; } ReturnCode = PopulateSubsystemDeviceIdList(ppDimms, DimmCount, SubsystemDeviceIdList, &SubsystemDeviceIdListCount, pDiagState); if (EFI_ERROR(ReturnCode) || (SubsystemDeviceIdListCount == 0)) { NVDIMM_DBG("The functionality to populate subsystem device Id list failed."); goto Finish; } for (Index = 0; Index < SubsystemDeviceIdListCount; ++Index) { ReturnCode = GetOptimumFwVersion(ppDimms, DimmCount, SubsystemDeviceIdList[Index], &OptimumFwVersion[Index], pDiagState); if (EFI_ERROR(ReturnCode)) { goto Finish; } ConvertFwVersion(OptimumFwVersionStr, OptimumFwVersion[Index].FwProduct, OptimumFwVersion[Index].FwRevision, OptimumFwVersion[Index].FwSecurityVersion, OptimumFwVersion[Index].FwBuild); for (Index1 = 0; Index1 < DimmCount; Index1++) { if (ppDimms[Index1] == NULL) { ReturnCode = EFI_INVALID_PARAMETER; *pDiagState |= DIAG_STATE_MASK_ABORTED; goto Finish; } if (ppDimms[Index1]->SubsystemDeviceId == SubsystemDeviceIdList[Index]) { ConvertFwVersion(TmpFwVerStr, ppDimms[Index1]->FwVer.FwProduct, ppDimms[Index1]->FwVer.FwRevision, ppDimms[Index1]->FwVer.FwSecurityVersion, ppDimms[Index1]->FwVer.FwBuild); if (StrCmp(OptimumFwVersionStr, TmpFwVerStr) != 0) { ReturnCode = GetDimmUid(ppDimms[Index1], DimmUid, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = GetPreferredValueAsString(ppDimms[Index1]->DeviceHandle.AsUint32, DimmUid, DimmIdPreference == DISPLAY_DIMM_ID_HANDLE, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } pAppendedDimmsStr = CatSPrintClean(pAppendedDimmsStr, (pAppendedDimmsStr == NULL) ? FORMAT_STR : L", " FORMAT_STR, DimmStr); } } } if (pAppendedDimmsStr != NULL) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_FW_INCONSISTENT), EVENT_CODE_902, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState, pAppendedDimmsStr, SubsystemDeviceIdList[Index], OptimumFwVersionStr); FREE_POOL_SAFE(pAppendedDimmsStr); } } Finish: FREE_POOL_SAFE(pAppendedDimmsStr); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check viral policy consistency for the specified DIMMs, and accordingly append to the fw diagnostics result. Also, accordingly modifies the test-state. @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[in out] ppResultStr Pointer to the result string of fw diagnostics message @param[out] pDiagState Pointer to the fw diagnostics test state. Possible states: DIAG_STATE_MASK_OK, DIAG_STATE_MASK_WARNING, DIAG_STATE_MASK_FAILED, DIAG_STATE_MASK_ABORTED @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL @retval EFI_OUT_OF_RESOURCES when memory allocation fails. **/ EFI_STATUS CheckViralPolicyConsistency( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN OUT CHAR16 **ppResultStr, OUT UINT8 *pDiagState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM_INFO *pDimms = NULL; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; UINTN Index = 0; UINT8 ViralPolicyState = 0; NVDIMM_ENTRY(); if (DimmCount == 0 || ppDimms == NULL || DimmCount > MAX_DIMMS || ppResultStr == NULL || pDiagState == NULL) { ReturnCode = EFI_INVALID_PARAMETER; if (pDiagState != NULL) { *pDiagState |= DIAG_STATE_MASK_ABORTED; } goto Finish; } /** make sure we can access the config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_DEVICE_ERROR; *pDiagState |= DIAG_STATE_MASK_ABORTED; NVDIMM_WARN("Unable to access protocol."); goto Finish; } pDimms = AllocateZeroPool(sizeof(*pDimms) * DimmCount); if (pDimms == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Could not allocate memory"); goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetDimms(pNvmDimmConfigProtocol, DimmCount, DIMM_INFO_CATEGORY_VIRAL_POLICY, pDimms); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_ABORTED; NVDIMM_WARN("Failed to retrieve the DIMM inventory found in NFIT"); goto Finish; } for (Index = 0; Index < DimmCount; Index++) { /** ViralPolicyState equals to state of the first DIMM, rest of DIMMs must be in the same state **/ if (Index == 0) { ViralPolicyState = pDimms[0].ViralPolicyEnable; } if (pDimms[Index].ViralPolicyEnable != ViralPolicyState) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_FW_INCONSISTENT_VIRAL_POLICY), EVENT_CODE_906, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState); goto Finish; } } Finish: FREE_POOL_SAFE(pDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get the smart and health data and checks the Media Temperature, Controller Temperature and Spare Block thresholds. Log proper events in case of any error. @param[in] pDimm Pointer to the DIMM @param[in out] ppResult Pointer to the result string of fw diagnostics message @param[out] pDiagState Pointer to the quick diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL **/ EFI_STATUS ThresholdsCheck( IN DIMM *pDimm, IN OUT CHAR16 **ppResultStr, IN OUT UINT8 *pDiagState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; SMART_AND_HEALTH_INFO HealthInfo; UINT8 AlarmEnabled = 0; INT16 MediaTemperatureThreshold = 0; INT16 ControllerTemperatureThreshold = 0; INT16 PercentageRemainingThreshold = 0; NVDIMM_ENTRY(); ZeroMem(&HealthInfo, sizeof(HealthInfo)); if ((NULL == pDimm) || (NULL == pDiagState) || (NULL == ppResultStr)) { if (pDiagState != NULL) { *pDiagState |= DIAG_STATE_MASK_ABORTED; } ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = GetSmartAndHealth(NULL, pDimm->DimmID, &HealthInfo); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed to Get SMART Info from Dimm handle 0x%x", pDimm->DeviceHandle.AsUint32); *pDiagState |= DIAG_STATE_MASK_ABORTED; goto Finish; } //Temperature and capacity checks ReturnCode = GetAlarmThresholds(NULL, pDimm->DimmID, SENSOR_TYPE_MEDIA_TEMPERATURE, &MediaTemperatureThreshold, &AlarmEnabled, NULL); if (EFI_ERROR(ReturnCode)) { *pDiagState |= DIAG_STATE_MASK_ABORTED; NVDIMM_ERR("Failed to get %s alarm threshold Dimm handle 0x%x", MEDIA_TEMPERATURE_STR, pDimm->DeviceHandle.AsUint32); goto Finish; } if (FALSE != AlarmEnabled && HealthInfo.MediaTempShutdownThresh < MediaTemperatureThreshold) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_FW_MEDIA_TEMPERATURE_THRESHOLD_ERROR), EVENT_CODE_903, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState, pDimm->DeviceHandle.AsUint32, MediaTemperatureThreshold, HealthInfo.MediaTempShutdownThresh); } ReturnCode = GetAlarmThresholds(NULL, pDimm->DimmID, SENSOR_TYPE_CONTROLLER_TEMPERATURE, &ControllerTemperatureThreshold, &AlarmEnabled, NULL); if (EFI_ERROR(ReturnCode)) { *pDiagState |= DIAG_STATE_MASK_ABORTED; NVDIMM_ERR("Failed to get %s alarm threshold Dimm handle 0x%x", CONTROLLER_TEMPERATURE_STR, pDimm->DeviceHandle.AsUint32); goto Finish; } if (FALSE != AlarmEnabled && HealthInfo.ContrTempShutdownThresh < ControllerTemperatureThreshold) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_FW_CONTROLLER_TEMPERATURE_THRESHOLD_ERROR), EVENT_CODE_904, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState, pDimm->DeviceHandle.AsUint32, ControllerTemperatureThreshold, HealthInfo.ContrTempShutdownThresh); } ReturnCode = GetAlarmThresholds(NULL, pDimm->DimmID, SENSOR_TYPE_PERCENTAGE_REMAINING, &PercentageRemainingThreshold, &AlarmEnabled, NULL); if (EFI_ERROR(ReturnCode)) { *pDiagState |= DIAG_STATE_MASK_ABORTED; NVDIMM_ERR("Failed to get %s alarm threshold Dimm handle 0x%x", SPARE_CAPACITY_STR, pDimm->DeviceHandle.AsUint32); goto Finish; } if (FALSE != AlarmEnabled && HealthInfo.PercentageRemainingValid && HealthInfo.PercentageRemaining < PercentageRemainingThreshold) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_FW_SPARE_BLOCK_THRESHOLD_ERROR), EVENT_CODE_905, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState, pDimm->DeviceHandle.AsUint32, HealthInfo.PercentageRemaining, PercentageRemainingThreshold); } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Diagnostics/FwDiagnostic.h000066400000000000000000000112371440615110200243300ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef FW_DIAGNOSTICS_H_ #define FW_DIAGNOSTICS_H_ #include "CoreDiagnostics.h" /** Run Fw diagnostics for the list of DIMMs, and appropriately populate the result messages, and test-state. @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[out] pResult Pointer of structure with diagnostics test result @retval EFI_SUCCESS Test executed correctly @retval EFI_DEVICE_ERROR Test wasn't executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. @retval EFI_OUT_OF_RESOURCES when memory allocation fails. **/ EFI_STATUS RunFwDiagnostics( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT8 DimmIdPreference, OUT DIAG_INFO *pResult ); /** Check firmware consistency for the specified DIMMs, and accordingly append to the fw diagnostics result. Also, accordingly modifies the test-state. @param[in] pDimm Pointer to the DIMM @param[in] pDimmStr Dimm string to be used in result messages @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[in out] ppResult Pointer to the result string of fw diagnostics message @param[out] pDiagState Pointer to the fw diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL @retval EFI_OUT_OF_RESOURCES when memory allocation fails. **/ EFI_STATUS CheckFwConsistency( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT8 DimmIdPreference, IN OUT CHAR16 **ppResultStr, OUT UINT8 *pDiagState ); /** Check viral policy consistency for the specified DIMMs, and accordingly append to the fw diagnostics result. Also, accordingly modifies the test-state. @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[in out] ppResultStr Pointer to the result string of fw diagnostics message @param[out] pDiagState Pointer to the fw diagnostics test state. Possible states: DIAG_STATE_MASK_OK, DIAG_STATE_MASK_WARNING, DIAG_STATE_MASK_FAILED, DIAG_STATE_MASK_ABORTED @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL @retval EFI_OUT_OF_RESOURCES when memory allocation fails. **/ EFI_STATUS CheckViralPolicyConsistency( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN OUT CHAR16 **ppResultStr, OUT UINT8 *pDiagState ); /** Populate the list of unique subsystem device IDs across all the specified DIMMs @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[out] SubsystemDeviceIdList Array of the unique subsystem device IDs @param[out] pSubsystemDeviceIdListCount Pointer to the count of unique subsystem device IDs @param[out] pDiagState Pointer to the fw diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS PopulateSubsystemDeviceIdList( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, OUT UINT16 SubsystemDeviceIdList[], OUT UINT16 *pSubsystemDeviceIdListCount, OUT UINT8 *pDiagState ); /** Determines the optimum firmware version for the specified list of DIMMs, for a particular subsystem device ID. @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[in] SubsystemDeviceId Specified subsystem device ID, to select the DIMMs for which to determine the optimum firmware version @param[out] pOptimumFwVer Pointer to the optimum firmware version @param[out] pDiagState Pointer to the fw diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. @retval EFI_DEVICE_ERROR Test wasn't executed correctly **/ EFI_STATUS GetOptimumFwVersion( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT16 SubsystemDeviceId, OUT FIRMWARE_VERSION *pOptimumFwVer, OUT UINT8 *pDiagState ); /** Get the smart and health data and checks the Media Temperature, Controller Temperature and Spare Block thresholds. Log proper events in case of any error. @param[in] pDimm Pointer to the DIMM @param[in out] ppResult Pointer to the result string of fw diagnostics message @param[out] pDiagState Pointer to the quick diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL **/ EFI_STATUS ThresholdsCheck( IN DIMM *pDimm, IN OUT CHAR16 **ppResultStr, IN OUT UINT8 *pDiagState ); #endif ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Diagnostics/QuickDiagnostic.c000066400000000000000000000503021440615110200250170ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "QuickDiagnostic.h" extern NVMDIMMDRIVER_DATA *gNvmDimmData; #define MANAGEABILITY_TEST_INDEX 0 #define BOOTSTATUS_TEST_INDEX 1 #define SMART_HEALTH_TEST_INDEX 2 /** Run quick diagnostics for list of DIMMs, and appropriately populate the result messages, and test-state. @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[out] pResult Pointer of structure with diagnostics test result @retval EFI_SUCCESS Test executed correctly @retval EFI_DEVICE_ERROR Test wasn't executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS RunQuickDiagnostics( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT8 DimmIdPreference, OUT DIAG_INFO *pResult ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CHAR16 DimmStr[MAX_DIMM_UID_LENGTH]; CHAR16 DimmUid[MAX_DIMM_UID_LENGTH]; UINT8 TmpDiagState = 0; UINT16 Index = 0; NVDIMM_ENTRY(); ZeroMem(DimmStr, sizeof(DimmStr)); ZeroMem(DimmUid, sizeof(DimmUid)); if (pResult == NULL || DimmCount > MAX_DIMMS) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ERR("The quick diagnostics test aborted due to an internal error."); goto Finish; } if (ppDimms == NULL || DimmCount == 0) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } for (Index = 0; Index < DimmCount; ++Index) { TmpDiagState = 0; if (ppDimms[Index] == NULL) { ReturnCode = EFI_INVALID_PARAMETER; continue; } ReturnCode = GetDimmUid(ppDimms[Index], DimmUid, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("GetDimmUid function for DIMM ID 0x%x failed.", ppDimms[Index]->DeviceHandle.AsUint32); continue; } ReturnCode = GetPreferredValueAsString(ppDimms[Index]->DeviceHandle.AsUint32, DimmUid, DimmIdPreference == DISPLAY_DIMM_ID_HANDLE, DimmStr, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("GetPreferredValueAsString function for DIMM ID 0x%x failed.", ppDimms[Index]->DeviceHandle.AsUint32); continue; } pResult->SubTestName[MANAGEABILITY_TEST_INDEX] = CatSPrint(NULL, L"Manageability"); ReturnCode = DiagnosticsManageabilityCheck(ppDimms[Index], DimmStr, &pResult->SubTestMessage[MANAGEABILITY_TEST_INDEX], &pResult->SubTestStateVal[MANAGEABILITY_TEST_INDEX]); if (EFI_ERROR(ReturnCode) || (!IsDimmManageable(ppDimms[Index]))) { NVDIMM_DBG("The check for manageability for DIMM ID 0x%x failed.", ppDimms[Index]->DeviceHandle.AsUint32); continue; } pResult->SubTestName[BOOTSTATUS_TEST_INDEX] = CatSPrint(NULL, L"Boot status"); ReturnCode = BootStatusDiagnosticsCheck(ppDimms[Index], DimmStr, &pResult->SubTestMessage[BOOTSTATUS_TEST_INDEX], &pResult->SubTestStateVal[BOOTSTATUS_TEST_INDEX]); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("The BSR check for DIMM ID 0x%x failed.", ppDimms[Index]->DeviceHandle.AsUint32); if ((pResult->SubTestStateVal[BOOTSTATUS_TEST_INDEX] & DIAG_STATE_MASK_ABORTED) != 0) { APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_QUICK_ABORTED_DIMM_INTERNAL_ERROR), EVENT_CODE_540, DIAG_STATE_MASK_ABORTED, &pResult->SubTestMessage[BOOTSTATUS_TEST_INDEX], &pResult->SubTestStateVal[BOOTSTATUS_TEST_INDEX], DimmStr); } continue; } pResult->SubTestName[SMART_HEALTH_TEST_INDEX] = CatSPrint(NULL, L"Health"); ReturnCode = SmartAndHealthCheck(ppDimms[Index], DimmStr, &pResult->SubTestMessage[SMART_HEALTH_TEST_INDEX], &pResult->SubTestStateVal[SMART_HEALTH_TEST_INDEX]); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("The smart and health check for DIMM ID 0x%x failed.", ppDimms[Index]->DeviceHandle.AsUint32); if ((TmpDiagState & DIAG_STATE_MASK_ABORTED) != 0) { APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_QUICK_ABORTED_DIMM_INTERNAL_ERROR), EVENT_CODE_540, DIAG_STATE_MASK_ABORTED, &pResult->SubTestMessage[SMART_HEALTH_TEST_INDEX], &pResult->SubTestStateVal[SMART_HEALTH_TEST_INDEX], DimmStr); } continue; } } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check manageability for a DIMM, and accordingly append to the quick diagnostics result. Also, accordingly modifies the test-state. @param[in] pDimm Pointer to the DIMM @param[in] pDimmStr Dimm string to be used in result messages @param[out] ppResult Pointer to the result string of quick diagnostics message @param[out] pDiagState Pointer to the quick diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL **/ EFI_STATUS DiagnosticsManageabilityCheck( IN DIMM *pDimm, IN CHAR16 *pDimmStr, IN OUT CHAR16 **ppResultStr, IN OUT UINT8 *pDiagState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 TmpFwApiVerStr[FW_API_VERSION_LEN]; NVDIMM_ENTRY(); if (pDimm == NULL || pDimmStr == NULL || ppResultStr == NULL || pDiagState == NULL) { if (pDiagState != NULL) { *pDiagState |= DIAG_STATE_MASK_ABORTED; } ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (!IsDimmManageable(pDimm)) { if (SPD_INTEL_VENDOR_ID != pDimm->SubsystemVendorId) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_UNMANAGEABLE_DIMM_SUBSYSTEM_VENDOR_ID), EVENT_CODE_501, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState, pDimmStr, EndianSwapUint16(pDimm->SubsystemVendorId)); } if (!IsSubsystemDeviceIdSupported(pDimm)) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_UNMANAGEABLE_DIMM_SUBSYSTEM_DEVICE_ID), EVENT_CODE_502, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState, pDimmStr, EndianSwapUint16(pDimm->SubsystemDeviceId)); } if (!IsFwApiVersionSupported(pDimm)) { ConvertFwApiVersion(TmpFwApiVerStr, pDimm->FwVer.FwApiMajor, pDimm->FwVer.FwApiMinor); APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_UNMANAGEABLE_DIMM_FW_API_VERSION), EVENT_CODE_503, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState, pDimmStr, TmpFwApiVerStr); } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Run SMART and health check for a DIMM, and accordingly append to the quick diagnostics result. Also, accordingly modifies the test-state. @param[in] pDimm Pointer to the DIMM @param[in] pDimmStr Dimm string to be used in result messages @param[out] ppResult Pointer to the result string of quick diagnostics message @param[out] pDiagState Pointer to the quick diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL **/ EFI_STATUS SmartAndHealthCheck( IN DIMM *pDimm, IN CHAR16 *pDimmStr, IN OUT CHAR16 **ppResultStr, IN OUT UINT8 *pDiagState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; SMART_AND_HEALTH_INFO HealthInfo; INT16 MediaTemperatureThreshold = 0; INT16 ControllerTemperatureThreshold = 0; INT16 PercentageRemainingThreshold = 0; UINT8 AlarmEnabled = 0; DIMM_INFO DimmInfo; CHAR16 *pActualHealthStr = NULL; CHAR16 *pActualHealthReasonStr = NULL; NVDIMM_ENTRY(); ZeroMem(&HealthInfo, sizeof(HealthInfo)); ZeroMem(&DimmInfo, sizeof(DimmInfo)); if (pDimm == NULL || pDimmStr == NULL || ppResultStr == NULL || pDiagState == NULL) { if (pDiagState != NULL) { *pDiagState |= DIAG_STATE_MASK_ABORTED; } ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = GetSmartAndHealth(NULL, pDimm->DimmID, &HealthInfo); if (EFI_ERROR(ReturnCode)) { if (EFI_NO_RESPONSE == ReturnCode) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_FW_BUSY), EVENT_CODE_541, DIAG_STATE_MASK_OK, ppResultStr, pDiagState, pDimm->DeviceHandle.AsUint32); goto Finish; } NVDIMM_DBG("Failed to Get SMART Info from Dimm 0x%x", pDimm->DeviceHandle.AsUint32); *pDiagState |= DIAG_STATE_MASK_ABORTED; goto Finish; } if (HealthInfo.LatchedLastShutdownStatus) { // LatchedLastShutdownStatus != 0 - Dirty Shutdown APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_DIRTY_SHUTDOWN), EVENT_CODE_530, DIAG_STATE_MASK_OK, ppResultStr, pDiagState, pDimm->DeviceHandle.AsUint32); } if (HealthInfo.HealthStatus != CONTROLLER_HEALTH_NORMAL) { if ((HealthInfo.HealthStatus & HealthStatusFatal) != 0) { pActualHealthStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_FATAL_FAILURE), NULL); } else if ((HealthInfo.HealthStatus & HealthStatusCritical) != 0) { pActualHealthStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_CRITICAL_FAILURE), NULL); } else if ((HealthInfo.HealthStatus & HealthStatusNoncritical) != 0) { pActualHealthStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_NON_CRITICAL_FAILURE), NULL); } else { pActualHealthStr = HiiGetString(gNvmDimmData->HiiHandle, STRING_TOKEN(STR_UNKNOWN), NULL); } if (HealthInfo.HealthStatusReason != HEALTH_STATUS_REASON_NONE) { ReturnCode = ConvertHealthStateReasonToHiiStr(gNvmDimmData->HiiHandle, HealthInfo.HealthStatusReason, &pActualHealthReasonStr); if (pActualHealthReasonStr == NULL || EFI_ERROR(ReturnCode)) { FREE_POOL_SAFE(pActualHealthStr); FREE_POOL_SAFE(pActualHealthReasonStr); NVDIMM_DBG("Error in converting health state reason to string"); goto Finish; } pActualHealthStr = CatSPrintClean(pActualHealthStr, FORMAT_STR_WITH_PARENTHESIS, pActualHealthReasonStr); } APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_BAD_HEALTH_STATE), EVENT_CODE_504, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState, pDimmStr, pActualHealthStr); FREE_POOL_SAFE(pActualHealthStr); FREE_POOL_SAFE(pActualHealthReasonStr); } else if ((pDimm->NvDimmStateFlags & BIT6) == BIT6) { // If BIT6 is set FW did not map a region to SPA on DIMM APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_ACPI_NVDIMM_SPA_NOT_MAPPED), EVENT_CODE_542, DIAG_STATE_MASK_OK, ppResultStr, pDiagState, pDimmStr); } ReturnCode = GetDimm(&gNvmDimmData->NvmDimmConfig, pDimm->DimmID, DIMM_INFO_CATEGORY_PACKAGE_SPARING | DIMM_INFO_CATEGORY_OPTIONAL_CONFIG_DATA_POLICY | DIMM_INFO_CATEGORY_FW_IMAGE_INFO | DIMM_INFO_CATEGORY_VIRAL_POLICY, &DimmInfo); if (EFI_ERROR(ReturnCode)) { *pDiagState |= DIAG_STATE_MASK_ABORTED; NVDIMM_DBG("Failed to get DIMM info for DimmID 0x%x", pDimm->DeviceHandle.AsUint32); goto Finish; } //Last Fw Update Status if (DimmInfo.LastFwUpdateStatus == FW_UPDATE_STATUS_FAILED) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_FW_LOAD_FAILED), EVENT_CODE_536, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } //Temperature and capacity checks ReturnCode = GetAlarmThresholds(NULL, pDimm->DimmID, SENSOR_TYPE_MEDIA_TEMPERATURE, &MediaTemperatureThreshold, &AlarmEnabled, NULL); if (EFI_ERROR(ReturnCode)) { *pDiagState |= DIAG_STATE_MASK_ABORTED; NVDIMM_DBG("Failed to get %s alarm threshold DimmID 0x%x", MEDIA_TEMPERATURE_STR, pDimm->DeviceHandle.AsUint32); goto Finish; } if (FALSE != AlarmEnabled && HealthInfo.MediaTemperatureValid && HealthInfo.MediaTemperature > MediaTemperatureThreshold) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_MEDIA_TEMP_EXCEEDS_ALARM_THR), EVENT_CODE_505, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState, pDimmStr, HealthInfo.MediaTemperature, MediaTemperatureThreshold); } ReturnCode = GetAlarmThresholds(NULL, pDimm->DimmID, SENSOR_TYPE_CONTROLLER_TEMPERATURE, &ControllerTemperatureThreshold, &AlarmEnabled, NULL); if (EFI_ERROR(ReturnCode)) { *pDiagState |= DIAG_STATE_MASK_ABORTED; NVDIMM_DBG("Failed to get %s alarm threshold DimmID 0x%x", CONTROLLER_TEMPERATURE_STR, pDimm->DeviceHandle.AsUint32); goto Finish; } if (FALSE != AlarmEnabled && HealthInfo.ControllerTemperatureValid && HealthInfo.ControllerTemperature > ControllerTemperatureThreshold) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_CONTROLLER_TEMP_EXCEEDS_ALARM_THR), EVENT_CODE_511, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState, pDimmStr, HealthInfo.ControllerTemperature, ControllerTemperatureThreshold); } ReturnCode = GetAlarmThresholds(NULL, pDimm->DimmID, SENSOR_TYPE_PERCENTAGE_REMAINING, &PercentageRemainingThreshold, &AlarmEnabled, NULL); if (EFI_ERROR(ReturnCode)) { *pDiagState |= DIAG_STATE_MASK_ABORTED; NVDIMM_DBG("Failed to get %s alarm threshold DimmID 0x%x", SPARE_CAPACITY_STR, pDimm->DeviceHandle.AsUint32); goto Finish; } if (FALSE != AlarmEnabled && HealthInfo.PercentageRemainingValid && HealthInfo.PercentageRemaining < PercentageRemainingThreshold) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_SPARE_CAPACITY_BELOW_ALARM_THR), EVENT_CODE_506, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState, pDimmStr, HealthInfo.PercentageRemaining, PercentageRemainingThreshold); } //Package spare availability check if ((DimmInfo.PackageSparingCapable == PACKAGE_SPARING_CAPABLE) && (DimmInfo.PackageSparesAvailable == PACKAGE_SPARES_NOT_AVAILABLE)) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_NO_PACKAGE_SPARES_AVAILABLE), EVENT_CODE_529, DIAG_STATE_MASK_WARNING, ppResultStr, pDiagState, pDimmStr); } //Viral state check if (DimmInfo.ViralStatus) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_VIRAL_STATE), EVENT_CODE_523, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } //AIT DRAM disabled check if (HealthInfo.AitDramEnabled == AIT_DRAM_DISABLED) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_AIT_DISABLED), EVENT_CODE_535, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Run boot status register check for a DIMM, and accordingly append to the quick diagnostics result. Also, accordingly modifies the test-state. @param[in] pDimm Pointer to the DIMM @param[in] pDimmStr Dimm string to be used in result messages @param[out] ppResult Pointer to the result string of quick diagnostics message @param[out] pDiagState Pointer to the quick diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL @retval EFI_DEVICE_ERROR Internal device error **/ EFI_STATUS BootStatusDiagnosticsCheck( IN DIMM *pDimm, IN CHAR16 *pDimmStr, IN OUT CHAR16 **ppResultStr, IN OUT UINT8 *pDiagState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM_BSR Bsr; BOOLEAN FIS_GTE_1_14 = FALSE; BOOLEAN FIS_GTE_2_01 = FALSE; UINT8 DdrtTrainingStatus = DDRT_TRAINING_UNKNOWN; UINT16 BSRStatusBitmask = 0; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; NVDIMM_ENTRY(); ZeroMem(&Bsr, sizeof(Bsr)); if (pDimm == NULL || pDimmStr == NULL || ppResultStr == NULL || pDiagState == NULL) { if (pDiagState != NULL) { *pDiagState |= DIAG_STATE_MASK_ABORTED; } ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /** make sure we can access the config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_DEVICE_ERROR; *pDiagState |= DIAG_STATE_MASK_ABORTED; NVDIMM_WARN("Unable to access protocol."); goto Finish; } /* Check to make sure the FW Version is bigger than 1.14*/ if ((pDimm->FwVer.FwApiMajor == 1 && pDimm->FwVer.FwApiMinor >= 14) || pDimm->FwVer.FwApiMajor > 1) { FIS_GTE_1_14 = TRUE; } /* Check to make sure the FW Version is bigger than 2.01*/ if ((pDimm->FwVer.FwApiMajor == 2 && pDimm->FwVer.FwApiMinor >= 1) || pDimm->FwVer.FwApiMajor > 2) { FIS_GTE_2_01 = TRUE; } ReturnCode = pNvmDimmConfigProtocol->GetBSRAndBootStatusBitMask(pNvmDimmConfigProtocol, pDimm->DimmID, &Bsr.AsUint64, &BSRStatusBitmask); if (EFI_ERROR(ReturnCode) || (BSRStatusBitmask & DIMM_BOOT_STATUS_UNKNOWN)) { ReturnCode = EFI_DEVICE_ERROR; NVDIMM_WARN("Unable to get the DIMMs BSR."); APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_BSR_NOT_READABLE), EVENT_CODE_513, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } else { if (Bsr.Separated_Current_FIS.Major == DIMM_BSR_MAJOR_NO_POST_CODE) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_BSR_BIOS_POST_TRAINING_FAILED), EVENT_CODE_519, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } else if (Bsr.Separated_Current_FIS.Major == DIMM_BSR_MAJOR_CHECKPOINT_INIT_FAILURE) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_BSR_FW_NOT_INITIALIZED), EVENT_CODE_520, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr, Bsr.Separated_Current_FIS.Major, Bsr.Separated_Current_FIS.Minor); } else if (Bsr.Separated_Current_FIS.Major == DIMM_BSR_ROM_MAJOR_CHECKPOINT_INIT_FAILURE) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_BSR_ROM_FAILED_INITIALIZATION), EVENT_CODE_545, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr, Bsr.Separated_Current_FIS.Major, Bsr.Separated_Current_FIS.Minor); } else if (Bsr.Separated_Current_FIS.Major == DIMM_BSR_MAJOR_CHECKPOINT_CPU_EXCEPTION) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_BSR_CPU_EXCEPTION), EVENT_CODE_537, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr, Bsr.Separated_Current_FIS.Major, Bsr.Separated_Current_FIS.Minor); } if (Bsr.Separated_Current_FIS.DT == DIMM_BSR_DDRT_IO_INIT_NOT_STARTED) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_BSR_DDRT_IO_NOT_STARTED), EVENT_CODE_544, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } GetDdrtIoInitInfo(NULL, pDimm->DimmID, &DdrtTrainingStatus); if (DdrtTrainingStatus == DDRT_TRAINING_UNKNOWN) { NVDIMM_DBG("Could not retrieve DDRT training status"); } if ((!FIS_GTE_2_01 && DdrtTrainingStatus != DDRT_TRAINING_COMPLETE && DdrtTrainingStatus != DDRT_S3_COMPLETE) || (FIS_GTE_2_01 && DdrtTrainingStatus != DDRT_TRAINING_COMPLETE && DdrtTrainingStatus != DDRT_S3_COMPLETE && DdrtTrainingStatus != NORMAL_MODE_COMPLETE)) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_BSR_DDRT_IO_NOT_COMPLETE), EVENT_CODE_538, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } if (Bsr.Separated_Current_FIS.MBR == DIMM_BSR_MAILBOX_NOT_READY) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_BSR_MAILBOX_NOT_READY), EVENT_CODE_539, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } if (Bsr.Separated_Current_FIS.DR != DIMM_BSR_AIT_DRAM_TRAINED_LOADED_READY) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_AIT_DRAM_NOT_READY), EVENT_CODE_533, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } if (FIS_GTE_1_14) { if ((Bsr.Separated_Current_FIS.DTS == DDRT_TRAINING_NOT_COMPLETE) || (Bsr.Separated_Current_FIS.DTS == DDRT_TRAINING_FAILURE)) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_DDRT_TRAINING_NOT_COMPLETE_FAILED), EVENT_CODE_543, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } } if (Bsr.Separated_Current_FIS.MR == DIMM_BSR_MEDIA_NOT_TRAINED) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_BSR_MEDIA_NOT_READY), EVENT_CODE_514, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } if (Bsr.Separated_Current_FIS.MR == DIMM_BSR_MEDIA_ERROR) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_BSR_MEDIA_ERROR), EVENT_CODE_515, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } if (Bsr.Separated_Current_FIS.MD == DIMM_BSR_MEDIA_DISABLED) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_BSR_MEDIA_DISABLED), EVENT_CODE_534, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } if (Bsr.Separated_Current_FIS.RR == DIMM_BSR_REBOOT_REQUIRED) { APPEND_RESULT_TO_THE_LOG(pDimm, STRING_TOKEN(STR_QUICK_BSR_REBOOT_REQUIRED), EVENT_CODE_507, DIAG_STATE_MASK_FAILED, ppResultStr, pDiagState, pDimmStr); } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Diagnostics/QuickDiagnostic.h000066400000000000000000000055441440615110200250340ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef QUICK_DIAGNOSTICS_H_ #define QUICK_DIAGNOSTICS_H_ #include "CoreDiagnostics.h" /** Run quick diagnostics for the list of DIMMs, and appropriately populate the result messages, and test-state. @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[out] pResult Pointer of structure with diagnostics test result @retval EFI_SUCCESS Test executed correctly @retval EFI_DEVICE_ERROR Test wasn't executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS RunQuickDiagnostics( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT8 DimmIdPreference, OUT DIAG_INFO *pResult ); /** Check manageability for a DIMM, and accordingly append to the quick diagnostics result. Also, accordingly modifies the test-state. @param[in] pDimm Pointer to the DIMM @param[in] pDimmStr Dimm string to be used in result messages @param[out] ppResult Pointer to the result string of quick diagnostics message @param[out] pDiagState Pointer to the quick diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL **/ EFI_STATUS DiagnosticsManageabilityCheck( IN DIMM *pDimm, IN CHAR16 *pDimmStr, IN OUT CHAR16 **ppResultStr, IN OUT UINT8 *pDiagState ); /** Run SMART and health check for a DIMM, and accordingly append to the quick diagnostics result. Also, accordingly modifies the test-state. @param[in] pDimm Pointer to the DIMM @param[in] pDimmStr Dimm string to be used in result messages @param[out] ppResult Pointer to the result string of quick diagnostics message @param[out] pDiagState Pointer to the quick diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL **/ EFI_STATUS SmartAndHealthCheck( IN DIMM *pDimm, IN CHAR16 *pDimmStr, IN OUT CHAR16 **ppResultStr, IN OUT UINT8 *pDiagState ); /** Run boot status register check for a DIMM, and accordingly append to the quick diagnostics result. Also, accordingly modifies the test-state. @param[in] pDimm Pointer to the DIMM @param[in] pDimmStr Dimm string to be used in result messages @param[out] ppResult Pointer to the result string of quick diagnostics message @param[out] pDiagState Pointer to the quick diagnostics test state @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL @retval EFI_DEVICE_ERROR Internal device error **/ EFI_STATUS BootStatusDiagnosticsCheck( IN DIMM *pDimm, IN CHAR16 *pDimmStr, IN OUT CHAR16 **ppResultStr, IN OUT UINT8 *pDiagState ); #endif ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Diagnostics/SecurityDiagnostic.c000066400000000000000000000101201440615110200255440ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "SecurityDiagnostic.h" extern NVMDIMMDRIVER_DATA *gNvmDimmData; #define ENCRYPTION_TEST_INDEX 0 #define INCONSISTENCY_TEST_INDEX 1 /** Run security diagnostics for the list of DIMMs, and appropriately populate the result structure. @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[out] pResult Pointer of structure with diagnostics test result @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS RunSecurityDiagnostics( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT8 DimmIdPreference, OUT DIAG_INFO *pResult ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CHAR16 *pInconsistentSecurityStatesStr = NULL; UINT8 DimmSecurityState = 0; UINT32 SecurityFlag = 0; UINT8 SecurityStateCount[SECURITY_STATES_COUNT]; BOOLEAN InconsistencyFlag = FALSE; UINT8 Index = 0; NVDIMM_ENTRY(); ZeroMem(SecurityStateCount, sizeof(SecurityStateCount)); if (pResult == NULL || DimmCount > MAX_DIMMS) { NVDIMM_DBG("The security diagnostics test aborted due to an internal error."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (DimmCount == 0 || ppDimms == NULL || GetManageableDimmsCount(ppDimms, DimmCount) == 0) { ReturnCode = EFI_SUCCESS; APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_SECURITY_NO_MANAGEABLE_DIMMS), EVENT_CODE_801, DIAG_STATE_MASK_OK, &pResult->Message, &pResult->StateVal); goto Finish; } pResult->SubTestName[ENCRYPTION_TEST_INDEX] = CatSPrint(NULL, L"Encryption status"); pResult->SubTestName[INCONSISTENCY_TEST_INDEX] = CatSPrint(NULL, L"Inconsistency"); for (Index = 0; Index < DimmCount; ++Index) { if (ppDimms[Index] == NULL) { ReturnCode = EFI_INVALID_PARAMETER; APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_SECURITY_ABORTED_INTERNAL_ERROR), EVENT_CODE_805, DIAG_STATE_MASK_ABORTED, &pResult->SubTestMessage[ENCRYPTION_TEST_INDEX], &pResult->SubTestStateVal[ENCRYPTION_TEST_INDEX]); goto Finish; } if (ppDimms[Index]->SkuInformation.EncryptionEnabled == MODE_DISABLED) { APPEND_RESULT_TO_THE_LOG(ppDimms[Index], STRING_TOKEN(STR_SECURITY_NOT_SUPPORTED), EVENT_CODE_804, DIAG_STATE_MASK_OK, &pResult->SubTestMessage[ENCRYPTION_TEST_INDEX], &pResult->SubTestStateVal[ENCRYPTION_TEST_INDEX]); } ReturnCode = GetDimmSecurityState( ppDimms[Index], PT_TIMEOUT_INTERVAL, &SecurityFlag); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetDimmSecurityState of DIMM ID 0x%x", ppDimms[Index]->DeviceHandle.AsUint32); APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_SECURITY_ABORTED_INTERNAL_ERROR), EVENT_CODE_805, DIAG_STATE_MASK_ABORTED, &pResult->SubTestMessage[INCONSISTENCY_TEST_INDEX], &pResult->SubTestStateVal[INCONSISTENCY_TEST_INDEX]); goto Finish; } ConvertSecurityBitmask(SecurityFlag, &DimmSecurityState); // increase the count of the security state that the dimm is currently in SecurityStateCount[DimmSecurityState]++; DimmSecurityState = 0; } for (Index = 0; Index < SECURITY_STATES_COUNT; Index++) { if ((SecurityStateCount[Index] > 0) && (SecurityStateCount[Index] != DimmCount)) { pInconsistentSecurityStatesStr = CatSPrintClean(pInconsistentSecurityStatesStr, FORMAT_STR L"%d " FORMAT_STR, InconsistencyFlag ? L", " : L"", SecurityStateCount[Index], SecurityToString(gNvmDimmData->HiiHandle, Index)); InconsistencyFlag = TRUE; } } if (InconsistencyFlag) { APPEND_RESULT_TO_THE_LOG(NULL, STRING_TOKEN(STR_SECURITY_INCONSISTENT), EVENT_CODE_802, DIAG_STATE_MASK_WARNING, &pResult->SubTestMessage[INCONSISTENCY_TEST_INDEX], &pResult->SubTestStateVal[INCONSISTENCY_TEST_INDEX], pInconsistentSecurityStatesStr); } ReturnCode = EFI_SUCCESS; goto Finish; Finish: FREE_POOL_SAFE(pInconsistentSecurityStatesStr); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Diagnostics/SecurityDiagnostic.h000066400000000000000000000014541440615110200255630ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef SECURITY_DIAGNOSTICS_H_ #define SECURITY_DIAGNOSTICS_H_ #include "CoreDiagnostics.h" /** Run security diagnostics for the list of DIMMs, and appropriately populate the result messages, and test-state. @param[in] ppDimms The DIMM pointers list @param[in] DimmCount DIMMs count @param[in] DimmIdPreference Preference for Dimm ID display (UID/Handle) @param[out] pResult Pointer of structure with diagnostics test result @retval EFI_SUCCESS Test executed correctly @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ EFI_STATUS RunSecurityDiagnostics( IN DIMM **ppDimms, IN CONST UINT16 DimmCount, IN UINT8 DimmIdPreference, OUT DIAG_INFO *pResult ); #endif ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Dimm.c000066400000000000000000007537741440615110200204040ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifdef OS_BUILD #include #endif #include #include #include #include #include #include #include #include "Dimm.h" #include "Namespace.h" #include #include #include "AsmCommands.h" #include #include #include #ifdef OS_BUILD #include #include #endif #ifndef OS_BUILD #include "Smbus.h" #endif #define SMBIOS_TYPE_MEM_DEV 17 #define SMBIOS_TYPE_MEM_DEV_MAPPED_ADDR 20 #ifdef PCD_CACHE_ENABLED int gPCDCacheEnabled = 1; #else int gPCDCacheEnabled = 0; #endif extern NVMDIMMDRIVER_DATA *gNvmDimmData; CONST UINT64 gSupportedBlockSizes[SUPPORTED_BLOCK_SIZES_COUNT] = { 512, // 512 (default) 514, // 512+2 (DIX) 520, // 512+8 528, // 512+16 4096, // 512*8 4112, // (512+2)*8 (DIX) 4160, // (512+8)*8 4224 // (512+16)*8 }; #ifndef OS_BUILD extern EFI_GUID gDcpmmProtocolGuid; #endif #ifdef OS_BUILD /* * Function get the ini configuration only on the first call * * It returns TRUE in case of large payload access is disabled and FALSE otherwise */ BOOLEAN ConfigIsLargePayloadDisabled() { static BOOLEAN config_large_payload_initialized = FALSE; static UINT8 large_payload_disabled = 0; EFI_STATUS efi_status; EFI_GUID guid = { 0 }; UINTN size; if (config_large_payload_initialized) return large_payload_disabled; size = sizeof(large_payload_disabled); efi_status = GET_VARIABLE(INI_PREFERENCES_LARGE_PAYLOAD_DISABLED, guid, &size, &large_payload_disabled); if ((EFI_SUCCESS != efi_status) || (large_payload_disabled > 1)) return FALSE; config_large_payload_initialized = TRUE; return (BOOLEAN)large_payload_disabled; } /* * Function get the ini configuration only on the first call * * It returns TRUE in case of DDRT protocol access is disabled and FALSE otherwise */ BOOLEAN ConfigIsDdrtProtocolDisabled() { static BOOLEAN config_ddrt_protocol_initialized = FALSE; static UINT8 ddrt_protocol_disabled = 0; EFI_STATUS efi_status; EFI_GUID guid = { 0 }; UINTN size; if (config_ddrt_protocol_initialized) return ddrt_protocol_disabled; size = sizeof(ddrt_protocol_disabled); efi_status = GET_VARIABLE(INI_PREFERENCES_DDRT_PROTOCOL_DISABLED, guid, &size, &ddrt_protocol_disabled); if ((EFI_SUCCESS != efi_status) || (ddrt_protocol_disabled > 1)) return FALSE; config_ddrt_protocol_initialized = TRUE; return (BOOLEAN)ddrt_protocol_disabled; } #endif // OS_BUILD /** Global pointers to the new processor assembler commands: gClFlush has more than one implementation and we should store here the newest that the processor supports. If the pointers are still NULL - the processor does not support any of the existing implementations. **/ VOID (*gClFlush)( VOID *pLinearAddress ); #ifndef OS_BUILD struct { UINT32 Eax; union { struct { UINT32 Unused:23; BOOLEAN ClFlushOpt:1; // EBX.CLFLUSHOPT[bit 23] BOOLEAN ClWb:1; // EBX.CLWB[bit 24] UINT32 Unused2:7; } Separated; UINT32 AsUint32; } Ebx; UINT32 Ecx; UINT32 Edx; } CpuInfo; /** InitializeCpuCommands Checks what set of required instructions current processor supports and assigns proper function pointers. The detection of new instructions is made, following the document: Ref # 319433-022, chapter 11-1. **/ STATIC VOID InitializeCpuCommands( ) { SetMem(&CpuInfo, sizeof(CpuInfo), 0x0); AsmCpuidEcx(CPUID_NEWMEM_FUNCTIONS_EAX, CPUID_NEWMEM_FUNCTIONS_ECX, (UINT32 *)&CpuInfo.Eax, (UINT32 *)&CpuInfo.Ebx, (UINT32 *)&CpuInfo.Ecx, (UINT32 *)&CpuInfo.Edx); if (CpuInfo.Ebx.Separated.ClFlushOpt) { gClFlush = &AsmClFlushOpt; NVDIMM_DBG("Flushing assigned to ClFlushOpt."); } else { NVDIMM_DBG("Flushing assigned to ClFlush."); gClFlush = &AsmFlushCl; } } #endif /** !OS_BUILD **/ STATIC EFI_STATUS PollOnArsDeviceBusy(IN DIMM *pDimm, IN UINT32 TimeoutSecs); /** Get dimm by Dimm ID Scan the dimm list for a dimm identified by Dimm ID @param[in] DimmID: The SMBIOS Type 17 handle of the dimm @param[in] pDimms: The head of the dimm list @retval DIMM struct pointer if matching dimm has been found @retval NULL pointer if not found **/ DIMM * GetDimmByPid( IN UINT32 DimmID, IN LIST_ENTRY *pDimms ) { DIMM *pCurDimm = NULL; DIMM *pTargetDimm = NULL; LIST_ENTRY *pCurDimmNode = NULL; NVDIMM_ENTRY(); for (pCurDimmNode = GetFirstNode(pDimms); !IsNull(pDimms, pCurDimmNode); pCurDimmNode = GetNextNode(pDimms, pCurDimmNode)) { pCurDimm = DIMM_FROM_NODE(pCurDimmNode); if (pCurDimm != NULL && DimmID == pCurDimm->DimmID) { pTargetDimm = pCurDimm; break; } } NVDIMM_EXIT(); return pTargetDimm; } /** Get dimm by serial number Scan the dimm list for a dimm identified by serial number @param[in] pDimms The head of the dimm list @param[in] DimmID The serial number of the dimm @retval DIMM struct pointer if matching dimm has been found @retval NULL pointer if not found **/ DIMM * GetDimmByHandle( IN UINT32 DeviceHandle, IN LIST_ENTRY *pDimms ) { DIMM *pCurDimm = NULL; DIMM *pTargetDimm = NULL; LIST_ENTRY *pCurDimmNode = NULL; NVDIMM_ENTRY(); for (pCurDimmNode = GetFirstNode(pDimms); !IsNull(pDimms, pCurDimmNode); pCurDimmNode = GetNextNode(pDimms, pCurDimmNode)) { pCurDimm = DIMM_FROM_NODE(pCurDimmNode); if (DeviceHandle == pCurDimm->DeviceHandle.AsUint32) { pTargetDimm = pCurDimm; break; } } NVDIMM_EXIT(); return pTargetDimm; } /** Get dimm by serial number Scan the dimm list for a dimm identified by serial number @param[in] pDimms The head of the dimm list @param[in] DimmID The serial number of the dimm @retval DIMM struct pointer if matching dimm has been found @retval NULL pointer if not found **/ DIMM * GetDimmBySerialNumber( IN LIST_ENTRY *pDimms, IN UINT32 SerialNumber ) { DIMM *pCurDimm = NULL; DIMM *pTargetDimm = NULL; LIST_ENTRY *pCurDimmNode = NULL; NVDIMM_ENTRY(); LIST_FOR_EACH(pCurDimmNode, pDimms) { pCurDimm = DIMM_FROM_NODE(pCurDimmNode); if (pCurDimm->SerialNumber == SerialNumber) { pTargetDimm = pCurDimm; break; } } NVDIMM_EXIT(); return pTargetDimm; } /** Get dimm by its unique identifier structure Scan the dimm list for a dimm identified by its unique identifier structure @param[in] pDimms The head of the dimm list @param[in] DimmUniqueId The unique identifier structure of the dimm @retval DIMM struct pointer if matching dimm has been found @retval NULL pointer if not found **/ DIMM * GetDimmByUniqueIdentifier( IN LIST_ENTRY *pDimms, IN DIMM_UNIQUE_IDENTIFIER DimmUniqueId ) { DIMM *pCurDimm = NULL; DIMM *pTargetDimm = NULL; LIST_ENTRY *pCurDimmNode = NULL; NVDIMM_ENTRY(); LIST_FOR_EACH(pCurDimmNode, pDimms) { pCurDimm = DIMM_FROM_NODE(pCurDimmNode); if ((pCurDimm->VendorId == DimmUniqueId.ManufacturerId) && (pCurDimm->SerialNumber == DimmUniqueId.SerialNumber) && (pCurDimm->ManufacturingInfoValid ? ((pCurDimm->ManufacturingLocation == DimmUniqueId.ManufacturingLocation) && (pCurDimm->ManufacturingDate == DimmUniqueId.ManufacturingDate)): TRUE)) { pTargetDimm = pCurDimm; break; } } NVDIMM_EXIT(); return pTargetDimm; } /** Get DIMM by index in global structure @param[in] DimmIndex - Index @param[in] pDev - pointer to global structure @retval **/ DIMM * GetDimmByIndex( IN INT32 DimmIndex, IN PMEM_DEV *pDev ) { DIMM *pCurDimm = NULL; DIMM *pTargetDimm = NULL; LIST_ENTRY *pCurDimmNode = NULL; INT32 Index = 0; NVDIMM_ENTRY(); for (pCurDimmNode = GetFirstNode(&pDev->Dimms); !IsNull(&pDev->Dimms, pCurDimmNode); pCurDimmNode = GetNextNode(&pDev->Dimms, pCurDimmNode)) { pCurDimm = DIMM_FROM_NODE(pCurDimmNode); if (Index == DimmIndex) { pTargetDimm = pCurDimm; break; } Index++; } NVDIMM_EXIT(); return pTargetDimm; } /** Get max Dimm ID Scan the dimm list for a max Dimm ID @param[in] pDimms: The head of the dimm list @retval Max Dimm ID or 0 if not found **/ UINT16 GetMaxPid( IN LIST_ENTRY *pDimms ) { UINT16 MaxPid = 0; DIMM *pCurDimm = NULL; LIST_ENTRY *pCurDimmNode = NULL; NVDIMM_ENTRY(); if (pDimms == NULL) { goto Finish; } LIST_FOR_EACH(pCurDimmNode, pDimms) { pCurDimm = DIMM_FROM_NODE(pCurDimmNode); if (pCurDimm->DimmID > MaxPid) { MaxPid = pCurDimm->DimmID; } } Finish: NVDIMM_EXIT(); return MaxPid; } /** Print memory map list. Use for debug purposes only @param[in] pMemmap: List head containing memmap range items **/ VOID PrintDimmMemmap( IN LIST_ENTRY *pMemmap ) { LIST_ENTRY *pNode = NULL; MEMMAP_RANGE *pRange = NULL; UINT16 Index = 0; NVDIMM_ENTRY(); if (pMemmap == NULL) { return; } NVDIMM_DBG("DIMM Memmap:"); //display the memmap LIST_FOR_EACH(pNode, pMemmap) { pRange = MEMMAP_RANGE_FROM_NODE(pNode); Index++; NVDIMM_DBG("#%d %12llx - %12llx (%12llx) ", Index, pRange->RangeStartDpa, pRange->RangeStartDpa + pRange->RangeLength - 1, pRange->RangeLength); switch (pRange->RangeType) { case MEMMAP_RANGE_VOLATILE: NVDIMM_DBG("VOLATILE\n"); break; case MEMMAP_RANGE_RESERVED: NVDIMM_DBG("RESERVED\n"); break; case MEMMAP_RANGE_PERSISTENT: NVDIMM_DBG("PERSISTENT\n"); break; case MEMMAP_RANGE_IS: NVDIMM_DBG("INTERLEAVE SET\n"); break; case MEMMAP_RANGE_IS_NOT_INTERLEAVED: NVDIMM_DBG("IS_NOT_INTERLEAVED\n"); break; case MEMMAP_RANGE_APPDIRECT_NAMESPACE: NVDIMM_DBG("APPDIRECT NAMESPACE\n"); break; case MEMMAP_RANGE_LAST_USABLE_DPA: NVDIMM_DBG("LAST USABLE DPA\n"); break; case MEMMAP_RANGE_FREE: NVDIMM_DBG("FREE\n"); break; default: NVDIMM_DBG("UNKNOWN\n"); break; } } NVDIMM_EXIT(); } VOID ShowDimmMemmap( IN DIMM *pDimm ) { LIST_ENTRY *pMemmapList = NULL; NVDIMM_ENTRY(); if (pDimm == NULL) { goto Finish; } pMemmapList = AllocateZeroPool(sizeof(*pMemmapList)); if (pMemmapList == NULL) { goto Finish; } InitializeListHead(pMemmapList); GetDimmMemmap(pDimm, pMemmapList); PrintDimmMemmap(pMemmapList); Finish: if (pMemmapList != NULL) { FreeMemmapItems(pMemmapList); FREE_POOL_SAFE(pMemmapList); } NVDIMM_EXIT(); } /** Add DIMM address space region to a linked list in appropriate place making sure target list will be already sorted by start DPA Function allocates memory for object with range item. It is caller responsibility to free this memory after it is no longer needed @param[in] pMemmapList Initialized list head to which region items will be added @param[in] pDimm Target DIMM structure pointer @param[in] Start Start address of a address range to be added @param[in] Length Length of address range to be added @param[in] Type of the range to be added (Interleave Set, Namespace, etc.) @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_OUT_OF_RESOURCES Not enough free space on target @retval EFI_SUCCESS List correctly retrieved **/ EFI_STATUS AddMemmapRange( IN LIST_ENTRY *pMemmapList, IN DIMM *pDimm, IN UINT64 Start, IN UINT64 Length, IN UINT32 Type ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; MEMMAP_RANGE *pMemmapRange = NULL; MEMMAP_RANGE *pCurrentRange = NULL; MEMMAP_RANGE *pNextRange = NULL; LIST_ENTRY *pNode = NULL; LIST_ENTRY *pNextNode = NULL; BOOLEAN Added = FALSE; if (pMemmapList == NULL || pDimm == NULL) { goto Finish; } pMemmapRange = (MEMMAP_RANGE *) AllocateZeroPool(sizeof(*pMemmapRange)); if (pMemmapRange == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pMemmapRange->Signature = MEMMAP_RANGE_SIGNATURE; pMemmapRange->pDimm = pDimm; pMemmapRange->RangeType = (UINT16)Type; pMemmapRange->RangeStartDpa = Start; pMemmapRange->RangeLength = Length; NVDIMM_VERB("New memmap range: start=%x length=%x", Start, Length); LIST_FOR_EACH(pNode, pMemmapList) { pCurrentRange = MEMMAP_RANGE_FROM_NODE(pNode); if (IsNodeAtEnd(pMemmapList, pNode)) { if (pMemmapRange->RangeStartDpa >= pCurrentRange->RangeStartDpa) { /** pMemmapRange->MemmapNode will be inserted after pNode, because pNode is treated as list head **/ InsertHeadList(pNode, &pMemmapRange->MemmapNode); NVDIMM_VERB("Add after the last node."); } else { /** pMemmapRange->MemmapNode will be inserted before pNode, because pNode is treated as list head **/ InsertTailList(pNode, &pMemmapRange->MemmapNode); NVDIMM_VERB("Add before the last node."); } Added = TRUE; break; } pNextNode = GetNextNode(pMemmapList, pNode); pNextRange = MEMMAP_RANGE_FROM_NODE(pNextNode); if (pMemmapRange->RangeStartDpa >= pCurrentRange->RangeStartDpa && pMemmapRange->RangeStartDpa < pNextRange->RangeStartDpa) { InsertHeadList(pNode, &pMemmapRange->MemmapNode); NVDIMM_VERB("Added in the middle"); Added = TRUE; break; } } if (!Added) { InsertTailList(pMemmapList, &pMemmapRange->MemmapNode); NVDIMM_VERB("Added at tail"); } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve list of memory regions of a DIMM Regions will be delivered in a form of sorted linked list with items containing start DPA and length of free ranges and they may overlap. Last item on the list will be a last DPA marker in order to point address boundary. @param[in] pDimm Target DIMM structure pointer @param[out] pMemmap Initialized list head to which region items will be added @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_OUT_OF_RESOURCES Not enough free space on target @retval EFI_SUCCESS List correctly retrieved **/ EFI_STATUS GetDimmMemmap( IN DIMM *pDimm, OUT LIST_ENTRY *pMemmap ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; UINT64 Offset = 0; DIMM_REGION *pDimmRegion = NULL; NAMESPACE *pNamespace = NULL; LIST_ENTRY *pNode = NULL; LIST_ENTRY *pNode2 = NULL; struct _NVM_IS *pIS = NULL; UINT64 Length = 0; UINT32 RegionCount = 0; BOOLEAN ISetInterleaved = FALSE; UINT32 Type = 0; NVDIMM_ENTRY(); if (pDimm == NULL || pMemmap == NULL) { goto Finish; } /** Volatile Partition might not start at DPA 0. For safety let's treat area starting at DPA 0 as Reserved **/ if (pDimm->VolatileStart > 0) { AddMemmapRange(pMemmap, pDimm, 0, pDimm->VolatileStart, MEMMAP_RANGE_RESERVED); } /** Volatile Partition **/ if (pDimm->VolatileCapacity > 0) { AddMemmapRange(pMemmap, pDimm, pDimm->VolatileStart, pDimm->VolatileCapacity, MEMMAP_RANGE_VOLATILE); } /** Persistent Partition **/ if (pDimm->PmCapacity > 0) { AddMemmapRange(pMemmap, pDimm, pDimm->PmStart, pDimm->PmCapacity, MEMMAP_RANGE_PERSISTENT); } /** At the end of Dimm may be reserved area **/ Offset = pDimm->VolatileStart + pDimm->VolatileCapacity + pDimm->PmCapacity; Length = pDimm->RawCapacity - Offset; if (Length > 0) { AddMemmapRange(pMemmap, pDimm, Offset, Length, MEMMAP_RANGE_RESERVED); } /** Interleave Sets **/ LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.ISs) { pIS = IS_FROM_NODE(pNode); ReturnCode = GetListSize(&pIS->DimmRegionList, &RegionCount); if (EFI_ERROR(ReturnCode) || RegionCount == 0) { goto Finish; } ISetInterleaved = RegionCount > 1; LIST_FOR_EACH(pNode2, &pIS->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pNode2); if (pDimmRegion->pDimm != pDimm) { continue; } Offset = pDimm->PmStart + pDimmRegion->PartitionOffset; if (ISetInterleaved) { Type = MEMMAP_RANGE_IS; } else { Type = MEMMAP_RANGE_IS_NOT_INTERLEAVED; } AddMemmapRange(pMemmap, pDimm, Offset, pDimmRegion->PartitionSize, Type); } } /** Namespaces **/ LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Namespaces) { pNamespace = NAMESPACE_FROM_NODE(pNode, NamespaceNode); for (Index = 0; Index < pNamespace->RangesCount; Index++) { if (pNamespace->Range[Index].pDimm != pDimm) { continue; } AddMemmapRange(pMemmap, pDimm, pNamespace->Range[Index].Dpa, pNamespace->Range[Index].Size, MEMMAP_RANGE_APPDIRECT_NAMESPACE); } } // Set last usable DPA to last PM partition address Offset = pDimm->PmStart + pDimm->PmCapacity; AddMemmapRange(pMemmap, pDimm, Offset, 0, MEMMAP_RANGE_LAST_USABLE_DPA); ReturnCode = EFI_SUCCESS; #ifdef MDEPKG_NDEBUG PrintDimmMemmap(pMemmap); #endif Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve list of free regions of a DIMM based on capacity type Free regions will be delivered in a form of sorted linked list with items containing start DPA and length of free ranges and they don't overlap each other @param[in] pDimm Target DIMM structure pointer @param[in] FreeCapacityTypeArg Determine a type of free capacity @param[out] pFreemap Initialized list head to which region items will be added @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_OUT_OF_RESOURCES Not enough free space on target @retval EFI_SUCCESS List correctly retrieved **/ EFI_STATUS GetDimmFreemap( IN DIMM *pDimm, IN FreeCapacityType FreeCapacityTypeArg, OUT LIST_ENTRY *pFreemap ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; MEMMAP_RANGE *pMemmapRange = NULL; LIST_ENTRY *pMemmapList = NULL; LIST_ENTRY *pUsableRanges = NULL; LIST_ENTRY *pOccupiedRanges = NULL; LIST_ENTRY *pNode = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || pFreemap == NULL) { goto Finish; } pMemmapList = (LIST_ENTRY *) AllocateZeroPool(sizeof(*pMemmapList)); if (pMemmapList == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } InitializeListHead(pMemmapList); pUsableRanges = (LIST_ENTRY *) AllocateZeroPool(sizeof(*pUsableRanges)); if (pUsableRanges == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } InitializeListHead(pUsableRanges); pOccupiedRanges = (LIST_ENTRY *) AllocateZeroPool(sizeof(*pOccupiedRanges)); if (pOccupiedRanges == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } InitializeListHead(pOccupiedRanges); ReturnCode = GetDimmMemmap(pDimm, pMemmapList); if (EFI_ERROR(ReturnCode)) { goto Finish; } LIST_FOR_EACH(pNode, pMemmapList) { pMemmapRange = MEMMAP_RANGE_FROM_NODE(pNode); /** Make list of ranges that can be used for specified mode. For example AppDirect Namespaces can be created only on Interleave Sets. Ranges may overlap and they will be sorted by DPA start address. **/ if (pMemmapRange->RangeType == MEMMAP_RANGE_PERSISTENT) { if (FreeCapacityTypeArg == FreeCapacityForPersistentRegion) { AddMemmapRange(pUsableRanges, pMemmapRange->pDimm, pMemmapRange->RangeStartDpa, pMemmapRange->RangeLength, pMemmapRange->RangeType); } } else if (pMemmapRange->RangeType == MEMMAP_RANGE_IS || pMemmapRange->RangeType == MEMMAP_RANGE_IS_NOT_INTERLEAVED) { if (FreeCapacityTypeArg == FreeCapacityForADMode) { AddMemmapRange(pUsableRanges, pMemmapRange->pDimm, pMemmapRange->RangeStartDpa, pMemmapRange->RangeLength, pMemmapRange->RangeType); } } /** Make list of used ranges for specified mode. Ranges may overlap and they will be sorted by DPA start address. **/ if (pMemmapRange->RangeType == MEMMAP_RANGE_APPDIRECT_NAMESPACE) { AddMemmapRange(pOccupiedRanges, pMemmapRange->pDimm, pMemmapRange->RangeStartDpa, pMemmapRange->RangeLength, pMemmapRange->RangeType); } } /** Get non-overlapped free ranges **/ ReturnCode = FindFreeRanges(pUsableRanges, pOccupiedRanges, pFreemap); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = EFI_SUCCESS; Finish: if (pOccupiedRanges != NULL) { FreeMemmapItems(pOccupiedRanges); FREE_POOL_SAFE(pOccupiedRanges); } if (pUsableRanges != NULL) { FreeMemmapItems(pUsableRanges); FREE_POOL_SAFE(pUsableRanges); } if (pMemmapList != NULL) { FreeMemmapItems(pMemmapList); FREE_POOL_SAFE(pMemmapList); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Free resources of memmap list items @param[in, out] pMemmapList Memmap list that items will be freed for **/ VOID FreeMemmapItems( IN OUT LIST_ENTRY *pMemmapList ) { MEMMAP_RANGE *pMemmapRange = NULL; LIST_ENTRY *pNode = NULL; LIST_ENTRY *pNext = NULL; NVDIMM_ENTRY(); if (pMemmapList == NULL) { goto Finish; } LIST_FOR_EACH_SAFE(pNode, pNext, pMemmapList) { pMemmapRange = MEMMAP_RANGE_FROM_NODE(pNode); RemoveEntryList(pNode); FREE_POOL_SAFE(pMemmapRange); } Finish: NVDIMM_EXIT(); } /** Merge overlapped ranges Memmap ranges may overlap each other. This function merges overlapped ranges to continuous ranges. Input list has to be sorted by DPA start address. Returned list will be sorted as well. The caller is responsible for a memory deallocation of the returned list. @param[in] pMemmapList Initialized list of ranges to merge. @param[out] pMergedList Initialized, output list to fill with continuous ranges. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS MergeMemmapItems( IN LIST_ENTRY *pMemmapList, OUT LIST_ENTRY *pMergedList ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LIST_ENTRY *pNode = NULL; MEMMAP_RANGE *pMemmapRange = NULL; UINT32 Index = 0; DIMM *pDimm = NULL; UINT64 RangeStartDpa = 0; UINT64 RangeEndDpa = 0; UINT64 RangeLength = 0; NVDIMM_ENTRY(); if (pMemmapList == NULL || pMergedList == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (!IsListEmpty(pMemmapList)) { Index = 0; LIST_FOR_EACH(pNode, pMemmapList) { pMemmapRange = MEMMAP_RANGE_FROM_NODE(pNode); if (Index == 0) { pDimm = pMemmapRange->pDimm; RangeStartDpa = pMemmapRange->RangeStartDpa; RangeLength = pMemmapRange->RangeLength; /** The End DPA will always be 1 less than the value obtained by adding the Range-Length to the Start DPA. **/ RangeEndDpa = pMemmapRange->RangeStartDpa + pMemmapRange->RangeLength - 1; } else if (pMemmapRange->RangeStartDpa <= RangeEndDpa) { /** Merging ranges **/ if ((pMemmapRange->RangeStartDpa + pMemmapRange->RangeLength - 1) > RangeEndDpa) { RangeEndDpa = pMemmapRange->RangeStartDpa + pMemmapRange->RangeLength - 1; RangeLength = RangeEndDpa - RangeStartDpa + 1; } } else { /** Separate, non-overlapped range **/ AddMemmapRange(pMergedList, pDimm, RangeStartDpa, RangeLength, MEMMAP_RANGE_UNDEFINED); RangeStartDpa = pMemmapRange->RangeStartDpa; RangeLength = pMemmapRange->RangeLength; RangeEndDpa = pMemmapRange->RangeStartDpa + pMemmapRange->RangeLength - 1; } Index++; } AddMemmapRange(pMergedList, pDimm, RangeStartDpa, RangeLength, MEMMAP_RANGE_UNDEFINED); } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Find free ranges Take list of usable ranges and subtract occupied ranges. The result will be list of free ranges. Input lists have to be sorted by DPA start address. Returned list will be sorted as well. The caller is responsible for a memory deallocation of the returned list. @param[in] pUsableRangesList Initialized list of usable ranges. @param[in] pOccupiedRangesList Initialized list of occupied ranges to subtract. @param[out] pFreeRangesList Initialized, output list to fill with free ranges. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS FindFreeRanges( IN LIST_ENTRY *pUsableRangesList, IN LIST_ENTRY *pOccupiedRangesList, OUT LIST_ENTRY *pFreeRangesList ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LIST_ENTRY *pUsableRangesListMerged = NULL; LIST_ENTRY *pOccupiedRangesListMerged = NULL; LIST_ENTRY *pNodeUsableRange = NULL; LIST_ENTRY *pNodeOccupiedRange = NULL; MEMMAP_RANGE *pUsableRange = NULL; MEMMAP_RANGE *pOccupiedRange = NULL; BOOLEAN UsableRangeDone = FALSE; DIMM *pDimm = NULL; UINT64 FreeRangeStartDpa = 0; UINT64 FreeRangeEndDpa = 0; UINT64 UsableRangeEndDpa = 0; UINT64 OccupiedRangeEndDpa = 0; NVDIMM_ENTRY(); if (pUsableRangesList == NULL || pOccupiedRangesList == NULL || pFreeRangesList == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pUsableRangesListMerged = (LIST_ENTRY *) AllocateZeroPool(sizeof(*pUsableRangesListMerged)); if (pUsableRangesListMerged == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } InitializeListHead(pUsableRangesListMerged); pOccupiedRangesListMerged = (LIST_ENTRY *) AllocateZeroPool(sizeof(*pOccupiedRangesListMerged)); if (pOccupiedRangesListMerged == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } InitializeListHead(pOccupiedRangesListMerged); /** First, merge overlapped ranges **/ ReturnCode = MergeMemmapItems(pUsableRangesList, pUsableRangesListMerged); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = MergeMemmapItems(pOccupiedRangesList, pOccupiedRangesListMerged); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Find free ranges **/ LIST_FOR_EACH(pNodeUsableRange, pUsableRangesListMerged) { pUsableRange = MEMMAP_RANGE_FROM_NODE(pNodeUsableRange); UsableRangeEndDpa = pUsableRange->RangeStartDpa + pUsableRange->RangeLength; UsableRangeDone = FALSE; pDimm = pUsableRange->pDimm; /** If there is no occupied ranges, then whole usable range is free **/ FreeRangeStartDpa = pUsableRange->RangeStartDpa; FreeRangeEndDpa = pUsableRange->RangeStartDpa + pUsableRange->RangeLength; /** Subtract occupied ranges from usable range **/ LIST_FOR_EACH(pNodeOccupiedRange, pOccupiedRangesListMerged) { pOccupiedRange = MEMMAP_RANGE_FROM_NODE(pNodeOccupiedRange); OccupiedRangeEndDpa = pOccupiedRange->RangeStartDpa + pOccupiedRange->RangeLength; if (pOccupiedRange->RangeStartDpa <= FreeRangeStartDpa) { /** Occupied range starts before usable range **/ if (OccupiedRangeEndDpa >= UsableRangeEndDpa) { /** Usable range is inside (or equal) occupied range, so there is no free range for this usable range **/ UsableRangeDone = TRUE; break; } else if (OccupiedRangeEndDpa > FreeRangeStartDpa) { /** Start free range where the occupied range ends **/ FreeRangeStartDpa = OccupiedRangeEndDpa; } else { /** Whole occupied range is before usable range, so they don't overlap **/ } } else { /** Occupied range starts after usable range **/ if (pOccupiedRange->RangeStartDpa > UsableRangeEndDpa) { /** Whole occupied range is after usable range, so free range ends where usable range ends **/ FreeRangeEndDpa = UsableRangeEndDpa; } else { /** Free range ends where occupied range starts **/ FreeRangeEndDpa = pOccupiedRange->RangeStartDpa; } /** Add found free range **/ AddMemmapRange(pFreeRangesList, pDimm, FreeRangeStartDpa, FreeRangeEndDpa - FreeRangeStartDpa, MEMMAP_RANGE_FREE); if (pOccupiedRange->RangeStartDpa >= UsableRangeEndDpa || OccupiedRangeEndDpa >= UsableRangeEndDpa) { /** Whole occupied range is after usable range, so no need to check next occupied ranges, because the list is sorted. **/ UsableRangeDone = TRUE; break; } else { /** Next free range starts where occupied range ends **/ FreeRangeStartDpa = OccupiedRangeEndDpa; } } } if (!UsableRangeDone) { /** The last occupied range ends before usable range end **/ FreeRangeEndDpa = UsableRangeEndDpa - FreeRangeStartDpa; AddMemmapRange(pFreeRangesList, pDimm, FreeRangeStartDpa, FreeRangeEndDpa, MEMMAP_RANGE_FREE); } } Finish: if (pUsableRangesListMerged != NULL) { FreeMemmapItems(pUsableRangesListMerged); FREE_POOL_SAFE(pUsableRangesListMerged); } if (pOccupiedRangesListMerged != NULL) { FreeMemmapItems(pOccupiedRangesListMerged); FREE_POOL_SAFE(pOccupiedRangesListMerged); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Remove the entire dimm inventory Remove the entire dimm inventory safely Dimms that cannot be removed safely are left in inventory @param[in,out] pDev: The pmem super structure **/ EFI_STATUS RemoveDimmInventory( IN OUT PMEM_DEV *pDev ) { DIMM *pCurDimm = NULL; LIST_ENTRY *pCurDimmNode = NULL; LIST_ENTRY *pTempDimmNode = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TmpReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); for (pCurDimmNode = GetFirstNode(&pDev->Dimms); !IsNull(&pDev->Dimms, pCurDimmNode) && pCurDimmNode != NULL; pCurDimmNode = pTempDimmNode) { pTempDimmNode = GetNextNode(&pDev->Dimms, pCurDimmNode); pCurDimm = DIMM_FROM_NODE(pCurDimmNode); RemoveEntryList(pCurDimmNode); TmpReturnCode = RemoveDimm(pCurDimm, 0); if (EFI_ERROR(TmpReturnCode)) { NVDIMM_WARN("Unable to remove NVDIMM %#x Error: %d", (NULL != pCurDimm) ? pCurDimm->DeviceHandle.AsUint32 : 0, TmpReturnCode); ReturnCode = TmpReturnCode; } } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } VOID InitializeDimmFieldsFromAcpiTables( IN NvDimmRegionMappingStructure *pNvDimmRegionTbl, IN ControlRegionTbl *pControlRegionTbl, IN ParsedPmttHeader *pPmttHead, OUT DIMM *pDimm ) { PMTT_MODULE_INFO *pPmttModuleInfo = NULL; pDimm->Signature = DIMM_SIGNATURE; pDimm->Configured = FALSE; pDimm->ISsNum = 0; if (pNvDimmRegionTbl != NULL) { /** ACPI 6.4, if BIT 31 of NfitDeviceHandle is set initialize DIMM fields from PMTT Previous versions of ACPI use NFIT only considering BIT 31 is zero **/ if (!(pNvDimmRegionTbl->DeviceHandle.AsUint32 & BIT31) || pPmttHead == NULL) { pDimm->SocketId = (UINT16)NFIT_NODE_SOCKET_TO_SOCKET_INDEX(pNvDimmRegionTbl->DeviceHandle.NfitDeviceHandle.NodeControllerId, pNvDimmRegionTbl->DeviceHandle.NfitDeviceHandle.SocketId); pDimm->DimmID = pNvDimmRegionTbl->NvDimmPhysicalId; pDimm->DeviceHandle.AsUint32 = pNvDimmRegionTbl->DeviceHandle.AsUint32; pDimm->ImcId = (UINT16)pNvDimmRegionTbl->DeviceHandle.NfitDeviceHandle.MemControllerId; pDimm->NodeControllerID = (UINT16)pNvDimmRegionTbl->DeviceHandle.NfitDeviceHandle.NodeControllerId; pDimm->ChannelId = (UINT16)pNvDimmRegionTbl->DeviceHandle.NfitDeviceHandle.MemChannel; pDimm->ChannelPos = (UINT16)pNvDimmRegionTbl->DeviceHandle.NfitDeviceHandle.DimmNumber; pDimm->NvDimmStateFlags = pNvDimmRegionTbl->NvDimmStateFlags; } else { if (pPmttHead != NULL) { if (!IS_ACPI_HEADER_REV_MAJ_0_MIN_2(pPmttHead->pPmtt)) { NVDIMM_DBG("Unexpected PMTT revision!"); return; } pPmttModuleInfo = GetDimmModuleByPidFromPmtt(pNvDimmRegionTbl->NvDimmPhysicalId, pPmttHead); if (pPmttModuleInfo == NULL) { NVDIMM_DBG("DIMM Module not found in PMTT"); return; } pDimm->SocketId = pPmttModuleInfo->CpuId; pDimm->DimmID = pPmttModuleInfo->SmbiosHandle; pDimm->DeviceHandle.AsUint32 = pNvDimmRegionTbl->DeviceHandle.AsUint32; pDimm->ImcId = pPmttModuleInfo->MemControllerId; pDimm->NodeControllerID = SOCKET_INDEX_TO_NFIT_NODE_ID(pPmttModuleInfo->SocketId); pDimm->ChannelId = pPmttModuleInfo->ChannelId; pDimm->ChannelPos = pPmttModuleInfo->SlotId; pDimm->NvDimmStateFlags = pPmttModuleInfo->Header.Flags; } } } if (pControlRegionTbl != NULL) { pDimm->VendorId = pControlRegionTbl->VendorId; pDimm->DeviceId = pControlRegionTbl->DeviceId; pDimm->Rid = pControlRegionTbl->Rid; pDimm->SubsystemVendorId = pControlRegionTbl->SubsystemVendorId; pDimm->SubsystemDeviceId = pControlRegionTbl->SubsystemDeviceId; pDimm->SubsystemRid = pControlRegionTbl->SubsystemRid; pDimm->ManufacturingInfoValid = pControlRegionTbl->ValidFields; pDimm->ManufacturingLocation = pControlRegionTbl->ManufacturingLocation; pDimm->ManufacturingDate = pControlRegionTbl->ManufacturingDate; pDimm->SerialNumber = pControlRegionTbl->SerialNumber; // Not using the rest of the control region fields } } /** Populate SMBUS fields in each DCPMM Note: Currently only needed for SPI flash recovery scenario in UEFI only @param[in] pNewDimm: input dimm structure to populate @retval EFI_SUCCESS Success @retval Other errors from called function **/ EFI_STATUS PopulateSmbusFields( IN DIMM *pNewDimm ) { EFI_STATUS ReturnCode = EFI_SUCCESS; pNewDimm->SmbusAddress.Cpu = (UINT8)(pNewDimm->DeviceHandle.NfitDeviceHandle.SocketId); pNewDimm->SmbusAddress.Imc = (UINT8)(pNewDimm->DeviceHandle.NfitDeviceHandle.MemControllerId); pNewDimm->SmbusAddress.Slot = (UINT8)(pNewDimm->DeviceHandle.NfitDeviceHandle.MemChannel * MAX_DIMMS_PER_CHANNEL + pNewDimm->DeviceHandle.NfitDeviceHandle.DimmNumber); //fill in fields provided by smbus. pNewDimm->Signature = DIMM_SIGNATURE; ReturnCode = EFI_SUCCESS; return ReturnCode; } /** Creates the DIMM inventory Using the Firmware Interface Table, create an in memory representation of each dimm. For each unique dimm call the initialization function unique to the type of DIMM. As each dimm is fully initialized add it to the in memory list of DIMMs @param[in,out] pDev: The pmem super structure @retval EFI_SUCCESS Success @retval EFI_... Other errors from subroutines **/ EFI_STATUS InitializeDimmInventory( IN OUT PMEM_DEV *pDev ) { EFI_STATUS ReturnCode = EFI_SUCCESS; ParsedFitHeader *pFitHead = NULL; ParsedPmttHeader *pPmttHead = NULL; NvDimmRegionMappingStructure **ppNvDimmRegionMappingStructures = NULL; DIMM *pNewDimm = NULL; UINT32 Index = 0; NVDIMM_ENTRY(); if (pDev == NULL || pDev->pFitHead == NULL || pDev->pFitHead->ppNvDimmRegionMappingStructures == NULL) { NVDIMM_DBG("Improperly initialized data"); return EFI_INVALID_PARAMETER; } #ifndef OS_BUILD InitializeCpuCommands(); #endif pFitHead = pDev->pFitHead; pPmttHead = pDev->pPmttHead; ppNvDimmRegionMappingStructures = pFitHead->ppNvDimmRegionMappingStructures; // Iterate over Region Mapping Structures (can be several per NVDIMM) // because they provide the NVDIMM physical ID, which is assigned by BIOS // and unique per boot. Could also use NFIT device handle. // Not iterating over control region tables (one per NVDIMM) because it // doesn't have any unique information other than the UID, but that isn't // as useful and takes longer to calculate and compare. for (Index = 0; Index < pFitHead->NvDimmRegionMappingStructuresNum; Index++) { if (GetDimmByPid(ppNvDimmRegionMappingStructures[Index]->NvDimmPhysicalId, &pDev->Dimms)) { // The associated NVDIMM physical ID is already in the dimms list, skip it continue; } // Create a new dimm struct for every NVDIMM, functional or not CHECK_RESULT_MALLOC(pNewDimm,(DIMM *) AllocateZeroPool(sizeof(*pNewDimm)), Finish); // Assume dimm is functional pNewDimm->NonFunctional = FALSE; // Fill in smbus address details CHECK_RESULT_CONTINUE(PopulateSmbusFields(pNewDimm)); // Insert into dimms list. We're only inserting a pointer so we can // continue editing the dimm struct InsertTailList(&pDev->Dimms, &pNewDimm->DimmNode); CHECK_RESULT(InitializeDimm(pNewDimm, pFitHead, pPmttHead, ppNvDimmRegionMappingStructures[Index]->NvDimmPhysicalId), ErrorInitializeDimm); continue; ErrorInitializeDimm: // If a dimm fails to initialize for any reason, it is also non-functional // for right now pNewDimm->NonFunctional = TRUE; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command Get Viral Policy Execute a FW command to check the security status of a DIMM @param[in] pDimm The DIMM to retrieve viral policy @param[out] pViralPolicyPayload buffer to retrieve DIMM FW response @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Parameter supplied is invalid @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval Various errors from FW **/ EFI_STATUS FwCmdGetViralPolicy( IN DIMM *pDimm, OUT PT_VIRAL_POLICY_PAYLOAD *pViralPolicyPayload ) { NVM_FW_CMD *pFwCmd = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pDimm == NULL || pViralPolicyPayload == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetAdminFeatures; pFwCmd->SubOpcode = SubopViralPolicy; pFwCmd->OutputPayloadSize = sizeof(*pViralPolicyPayload); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); NVDIMM_DBG("FW CMD Status %d", pFwCmd->Status); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending PtGetViralPolicy command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pViralPolicyPayload, sizeof(*pViralPolicyPayload), pFwCmd->OutPayload, sizeof(*pViralPolicyPayload)); Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Payload is the same for set and get operation **/ EFI_STATUS FwCmdGetOptionalConfigurationDataPolicy( IN DIMM *pDimm, OUT PT_OPTIONAL_DATA_POLICY_PAYLOAD *pOptionalDataPolicyPayload ) { NVM_FW_CMD *pFwCmd = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pDimm == NULL || pOptionalDataPolicyPayload == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetFeatures; pFwCmd->SubOpcode = SubopConfigDataPolicy; pFwCmd->OutputPayloadSize = sizeof(pOptionalDataPolicyPayload->Payload); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); NVDIMM_DBG("FW CMD Status %d", pFwCmd->Status); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending PtGetOptionalDataPolicy command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pOptionalDataPolicyPayload->Payload.Data, sizeof(pOptionalDataPolicyPayload->Payload), pFwCmd->OutPayload, sizeof(pOptionalDataPolicyPayload->Payload)); pOptionalDataPolicyPayload->FisMajor = pDimm->FwVer.FwApiMajor; pOptionalDataPolicyPayload->FisMinor = pDimm->FwVer.FwApiMinor; Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Payload is the same for set and get operation **/ EFI_STATUS FwCmdSetOptionalConfigurationDataPolicy( IN DIMM *pDimm, IN PT_OPTIONAL_DATA_POLICY_PAYLOAD *pOptionalDataPolicyPayload ) { NVM_FW_CMD *pFwCmd = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pDimm == NULL || pOptionalDataPolicyPayload == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtSetFeatures; pFwCmd->SubOpcode = SubopConfigDataPolicy; pFwCmd->InputPayloadSize = sizeof(pOptionalDataPolicyPayload->Payload); CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), pOptionalDataPolicyPayload->Payload.Data, pFwCmd->InputPayloadSize); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); NVDIMM_DBG("FW CMD Status %d", pFwCmd->Status); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending PtGetOptionalDataPolicy command (Dimm(%d), RC = " FORMAT_EFI_STATUS ", Status = %d)", pDimm->DeviceHandle.AsUint32 ,pFwCmd->Status ,ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command get security info Execute a FW command to check the security status of a DIMM @param[in] pDimm: The DIMM to retrieve security info on @param[out] pSecurityPayload: Area to place the security info returned from FW @param[in] DimmId: The SMBIOS table type 17 handle of the Intel NVM Dimm @retval EFI_SUCCESS: Success @retval EFI_OUT_OF_RESOURCES: memory allocation failure @retval Various errors from FW are still TBD **/ EFI_STATUS FwCmdGetSecurityInfo( IN DIMM *pDimm, OUT PT_GET_SECURITY_PAYLOAD *pSecurityPayload ) { NVM_FW_CMD *pFwCmd = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetSecInfo; pFwCmd->SubOpcode= SubopGetSecState; pFwCmd->OutputPayloadSize = sizeof(*pSecurityPayload); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending PtGetSecInfo command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pSecurityPayload, sizeof(*pSecurityPayload), pFwCmd->OutPayload, sizeof(*pSecurityPayload)); Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Is Firmware command get security Opt-In supported Get security opt-in command is supported for certain fw versions with certain opt-in codes @param[in] pDimm: The DIMM to send get security opt-in command @param[in] OptInCode: Opt-In Code that is requested status for @retval BOOLEAN: return if the command is supported for opt-in code **/ BOOLEAN IsGetSecurityOptInSupported( IN DIMM *pDimm, IN UINT16 OptInCode) { BOOLEAN FIS_GT_2_1 = FALSE; BOOLEAN FIS_GTE_2_3 = FALSE; FIS_GT_2_1 = ((2 <= pDimm->FwVer.FwApiMajor && 1 < pDimm->FwVer.FwApiMinor) || (3 <= pDimm->FwVer.FwApiMajor)); FIS_GTE_2_3 = ((2 <= pDimm->FwVer.FwApiMajor && 3 <= pDimm->FwVer.FwApiMinor) || (3 <= pDimm->FwVer.FwApiMajor)); //If Fis is not greater than 2.1 get security opt in is not supported if (!FIS_GT_2_1) { return FALSE; } //If Fis is 2.2 only s3 resume is supported if (FIS_GT_2_1 && !FIS_GTE_2_3 && OptInCode != NVM_S3_RESUME) { return FALSE; } return TRUE; } /** Firmware command get security Opt-In Execute a FW command to check the security Opt-In code of a DIMM @param[in] pDimm: The DIMM to retrieve security info on @param[in] OptInCode: Opt-In Code that is requested status for @param[out] pSecurityOptIn: Area to place the returned from FW @retval EFI_SUCCESS: Success @retval EFI_OUT_OF_RESOURCES: memory allocation failure @retval Various errors from FW are still TBD **/ EFI_STATUS FwCmdGetSecurityOptIn( IN DIMM *pDimm, IN UINT16 OptInCode, OUT PT_OUTPUT_PAYLOAD_GET_SECURITY_OPT_IN *pSecurityOptIn ) { NVM_FW_CMD *pFwCmd = NULL; PT_INPUT_PAYLOAD_GET_SECURITY_OPT_IN InputPayload; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pDimm == NULL || pSecurityOptIn == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if(!IsGetSecurityOptInSupported(pDimm,OptInCode)) { ReturnCode = EFI_UNSUPPORTED; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } SetMem(&InputPayload, sizeof(InputPayload), 0x0); pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetSecInfo; pFwCmd->SubOpcode = SubOpGetSecOptIn; InputPayload.OptInCode = OptInCode; pFwCmd->InputPayloadSize = sizeof(InputPayload); pFwCmd->OutputPayloadSize = sizeof(*pSecurityOptIn); CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), &InputPayload, pFwCmd->InputPayloadSize); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending PtGetSecOptIn command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pSecurityOptIn, sizeof(*pSecurityOptIn), pFwCmd->OutPayload, sizeof(*pSecurityOptIn)); if (pSecurityOptIn->OptInCode != OptInCode) { NVDIMM_DBG("Error detected when sending PtGetSecOptIn command (Requested OptInCode = %d , Received OptInCode = %d)", OptInCode, pSecurityOptIn->OptInCode); ReturnCode = EFI_NOT_FOUND; } Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to disable ARS @param[in] pDimm Pointer to the DIMM to disable ARS on @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval Various errors from FW **/ EFI_STATUS FwCmdDisableARS( IN DIMM *pDimm ) { NVM_FW_CMD *pFwCmd = NULL; PT_PAYLOAD_SET_ADDRESS_RANGE_SCRUB *pARSInputPayload = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pDimm == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtSetFeatures; pFwCmd->SubOpcode = SubopAddressRangeScrub; pARSInputPayload = (PT_PAYLOAD_SET_ADDRESS_RANGE_SCRUB*)pFwCmd->InputPayload; pARSInputPayload->Enable = 0; pFwCmd->InputPayloadSize = sizeof(PT_PAYLOAD_SET_ADDRESS_RANGE_SCRUB); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending Firmware Set AddressRangeScrub command (RC = " FORMAT_EFI_STATUS ", Status = %d)", ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } NVDIMM_DBG("Polling ARS long op status to verify ARS disabled completed."); ReturnCode = PollOnArsDeviceBusy(pDimm, DISABLE_ARS_TOTAL_TIMEOUT_SEC); NVDIMM_DBG("Finished polling long op, return val = %x", ReturnCode); Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** This helper function is used to determine the ARS status for the particular DIMM by inspecting the firmware ARS return payload. @param[in] pARSPayload Pointer to the ARS return payload @param[out] pDimmARSStatus Pointer to the individual DIMM ARS status @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ EFI_STATUS GetDimmARSStatusFromARSPayload( IN PT_PAYLOAD_ADDRESS_RANGE_SCRUB *pARSPayload, OUT UINT8 *pDimmARSStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pARSPayload == NULL || pDimmARSStatus == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("GetLongOpDimmInfoStatus failed!"); goto Finish; } *pDimmARSStatus = LONG_OP_STATUS_UNKNOWN; if ((pARSPayload->DPACurrentAddress == pARSPayload->DPAEndAddress) && !(pARSPayload->Enable)) { *pDimmARSStatus = LONG_OP_STATUS_COMPLETED; } else if ((pARSPayload->DPACurrentAddress > pARSPayload->DPAStartAddress) && (pARSPayload->DPACurrentAddress < pARSPayload->DPAEndAddress) && !(pARSPayload->Enable)) { *pDimmARSStatus = LONG_OP_STATUS_ABORTED; } else if ((pARSPayload->DPACurrentAddress == 0x00) || (pARSPayload->DPACurrentAddress == pARSPayload->DPAStartAddress)) { *pDimmARSStatus = LONG_OP_STATUS_NOT_STARTED; } else if ((pARSPayload->DPACurrentAddress > pARSPayload->DPAStartAddress) && (pARSPayload->Enable)) { *pDimmARSStatus = LONG_OP_STATUS_IN_PROGRESS; } else { *pDimmARSStatus = LONG_OP_STATUS_UNKNOWN; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to retrieve the ARS status of a particular DIMM. @param[in] pDimm Pointer to the DIMM to retrieve ARSStatus on @param[out] pDimmARSStatus Pointer to the individual DIMM ARS status @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval Various errors from FW **/ EFI_STATUS FwCmdGetARS( IN DIMM *pDimm, OUT UINT8 *pDimmARSStatus ) { NVM_FW_CMD *pFwCmd = NULL; PT_PAYLOAD_ADDRESS_RANGE_SCRUB *pARSPayload = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); CHECK_NULL_ARG(pDimm, Finish); CHECK_NULL_ARG(pDimmARSStatus, Finish); *pDimmARSStatus = LONG_OP_STATUS_UNKNOWN; pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetFeatures; pFwCmd->SubOpcode = SubopAddressRangeScrub; pFwCmd->OutputPayloadSize = sizeof(*pARSPayload); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending Firmware Get AddressRangeScrub command (RC = " FORMAT_EFI_STATUS ", Status = %d)", ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } pARSPayload = (PT_PAYLOAD_ADDRESS_RANGE_SCRUB *) pFwCmd->OutPayload; ReturnCode = GetDimmARSStatusFromARSPayload(pARSPayload, pDimmARSStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when retrieving ARSStatus from ARS Payload"); goto Finish; } Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command Device Characteristics @param[in] pDimm The Intel NVM Dimm to retrieve device characteristics info for @param[out] ppPayload Area to place returned info from FW The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more input parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS FwCmdDeviceCharacteristics ( IN DIMM *pDimm, OUT PT_DEVICE_CHARACTERISTICS_OUT **ppPayload ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || ppPayload == NULL) { goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } *ppPayload = AllocateZeroPool(sizeof(**ppPayload)); if (*ppPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtIdentifyDimm; pFwCmd->SubOpcode = SubopDeviceCharacteristics; pFwCmd->OutputPayloadSize = sizeof((*ppPayload)->Payload); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending Device Characteristics command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S((*ppPayload)->Payload.Data, sizeof((*ppPayload)->Payload), pFwCmd->OutPayload, sizeof((*ppPayload)->Payload)); (*ppPayload)->FisMajor = pDimm->FwVer.FwApiMajor; (*ppPayload)->FisMinor = pDimm->FwVer.FwApiMinor; ReturnCode = EFI_SUCCESS; Finish: if (EFI_ERROR(ReturnCode) && (NULL != ppPayload)) { FREE_POOL_SAFE(*ppPayload); } FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Execute Firmware command to Get DIMM Partition Info @param[in] pDimm The DIMM to retrieve security info on @param[out] pPayload Area to place the info returned from FW @retval EFI_INVALID_PARAMETER NULL pointer for DIMM structure provided @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval EFI_... Other errors from subroutines @retval EFI_SUCCESS Success **/ EFI_STATUS FwCmdGetDimmPartitionInfo( IN DIMM *pDimm, OUT PT_DIMM_PARTITION_INFO_PAYLOAD *pPayload ) { NVM_FW_CMD *pFwCmd = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pDimm == NULL || pPayload == NULL) { goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetAdminFeatures; pFwCmd->SubOpcode = SubopDimmPartitionInfo; pFwCmd->OutputPayloadSize = OUT_PAYLOAD_SIZE; ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending GetAdminFeatures command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); NVDIMM_DBG("FW CMD Status %d", pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pPayload, sizeof(*pPayload), pFwCmd->OutPayload, sizeof(*pPayload)); Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command access/read Platform Config Data using small payload only. The function allows to specify the requested data offset and the size. The function is going to allocate the ppRawData buffer if it is not allocated. The buffer's minimal size is the size of the Partition! @param[in] pDimm The Intel NVM Dimm to retrieve identity info on @param[in] PartitionId Partition number to get data from @param[in] ReqOffset Data read starting point @param[in] ReqDataSize Number of bytes to read @param[out] Pointer to the buffer pointer for storing retrieved data @retval EFI_SUCCESS: Success, otherwise: Error **/ EFI_STATUS FwGetPCDFromOffsetSmallPayload( IN DIMM *pDimm, IN UINT8 PartitionId, IN UINT32 ReqOffset, IN UINT32 ReqDataSize, OUT UINT8 **ppRawData) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; PT_INPUT_PAYLOAD_GET_PLATFORM_CONFIG_DATA InputPayload; UINT32 StartingPageOffset = ((ReqOffset / PCD_GET_SMALL_PAYLOAD_DATA_SIZE)*PCD_GET_SMALL_PAYLOAD_DATA_SIZE); UINT32 ReadOffset = 0; UINT32 PcdSize = 0; SetMem(&InputPayload, sizeof(InputPayload), 0x0); if (pDimm == NULL || ppRawData == NULL || 0 == ReqDataSize) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (PartitionId == PCD_OEM_PARTITION_ID) { PcdSize = pDimm->PcdOemPartitionSize; } else if (PartitionId == PCD_LSA_PARTITION_ID) { PcdSize = pDimm->PcdLsaPartitionSize; } else { ReturnCode = EFI_UNSUPPORTED; goto Finish; } /* * PcdSize is 0 if Media is disabled. * PcdSize was retrieved at driver load time so it is possible that since load time there * was a fatal media error that this would not catch. */ if (PcdSize == 0) { ReturnCode = FwCmdGetPlatformConfigDataSize(pDimm, PartitionId, &PcdSize); if (EFI_ERROR(ReturnCode) || PcdSize == 0) { NVDIMM_DBG("FW CMD Error: %d", ReturnCode); goto Finish; } else if (PartitionId == PCD_OEM_PARTITION_ID) { pDimm->PcdOemPartitionSize = PcdSize; } else if (PartitionId == PCD_LSA_PARTITION_ID) { pDimm->PcdLsaPartitionSize = PcdSize; } } if (PcdSize < (StartingPageOffset + ReqDataSize)) { return EFI_BUFFER_TOO_SMALL; } if (NULL == *ppRawData) { *ppRawData = AllocateZeroPool(PcdSize); if (*ppRawData == NULL) { NVDIMM_WARN("Can't allocate memory for Platform Config Data (%d bytes)", PcdSize); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } /** Retrieve the PCD/LSA data **/ pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetAdminFeatures; pFwCmd->SubOpcode = SubopPlatformDataInfo; InputPayload.PartitionId = PartitionId; InputPayload.CmdOptions.RetrieveOption = PCD_CMD_OPT_PARTITION_DATA; pFwCmd->InputPayloadSize = sizeof(InputPayload); /** Get PCD by small payload in loop in 128 byte chunks **/ pFwCmd->LargeOutputPayloadSize = 0; pFwCmd->OutputPayloadSize = PCD_GET_SMALL_PAYLOAD_DATA_SIZE; InputPayload.CmdOptions.PayloadType = PCD_CMD_OPT_SMALL_PAYLOAD; for (ReadOffset = StartingPageOffset; ReadOffset < (ReqOffset+ReqDataSize); ReadOffset += PCD_GET_SMALL_PAYLOAD_DATA_SIZE) { InputPayload.Offset = ReadOffset; CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), &InputPayload, pFwCmd->InputPayloadSize); ReturnCode = PassThru(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending Platform Config Data (Get Data) command (Offset = %d, RC = " FORMAT_EFI_STATUS ")", ReadOffset, ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(*ppRawData + ReadOffset, PcdSize - ReadOffset, pFwCmd->OutPayload, PCD_GET_SMALL_PAYLOAD_DATA_SIZE); } Finish: FREE_POOL_SAFE(pFwCmd); return ReturnCode; } /** Firmware command to get Partition Data using large payload. Execute a FW command to get information about DIMM regions and REGIONs configuration. The caller is responsible for a memory deallocation of the ppPlatformConfigData @param[in] pDimm The Intel NVM Dimm to retrieve identity info on @param[in] PartitionId Partition number to get data from @param[out] ppRawData Pointer to a new buffer pointer for storing retrieved data @retval EFI_SUCCESS: Success @retval EFI_OUT_OF_RESOURCES: memory allocation failure **/ EFI_STATUS FwCmdGetPcdLargePayload( IN DIMM *pDimm, IN UINT8 PartitionId, OUT UINT8 **ppRawData ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; PT_INPUT_PAYLOAD_GET_PLATFORM_CONFIG_DATA InputPayload; NVDIMM_ENTRY(); SetMem(&InputPayload, sizeof(InputPayload), 0x0); if (pDimm == NULL || ppRawData == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *ppRawData = AllocateZeroPool(PCD_PARTITION_SIZE); if (*ppRawData == NULL) { NVDIMM_WARN("Can't allocate memory for Platform Config Data (%u bytes)", PCD_PARTITION_SIZE); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } /** Retrieve the OEM PCD data **/ pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetAdminFeatures; pFwCmd->SubOpcode = SubopPlatformDataInfo; InputPayload.PartitionId = PartitionId; InputPayload.CmdOptions.RetrieveOption = PCD_CMD_OPT_PARTITION_DATA; pFwCmd->InputPayloadSize = sizeof(InputPayload); /** Get PCD by large payload in single call **/ pFwCmd->LargeOutputPayloadSize = PCD_PARTITION_SIZE; InputPayload.Offset = 0; InputPayload.CmdOptions.PayloadType = PCD_CMD_OPT_LARGE_PAYLOAD; CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), &InputPayload, pFwCmd->InputPayloadSize); #ifdef OS_BUILD ReturnCode = PassThru(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); #else ReturnCode = PassThruWithRetryOnFwAborted(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); #endif if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending Platform Config Data (Get Data) command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(*ppRawData, PCD_PARTITION_SIZE, pFwCmd->LargeOutputPayload, PCD_PARTITION_SIZE); Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command get Platform Config Data. Execute a FW command to get information about DIMM regions and REGIONs configuration. The caller is responsible for a memory deallocation of the ppPlatformConfigData @param[in] pDimm The Intel NVM Dimm to retrieve identity info on @param[in] PartitionId Partition number to get data from @param[out] ppRawData Pointer to a new buffer pointer for storing retrieved data @retval EFI_SUCCESS: Success @retval EFI_UNSUPPORTED: invalid partition specified (OEM Partition not supported). @retval EFI_OUT_OF_RESOURCES: memory allocation failure **/ EFI_STATUS FwCmdGetPlatformConfigData( IN DIMM *pDimm, IN UINT8 PartitionId, OUT UINT8 **ppRawData ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; PT_INPUT_PAYLOAD_GET_PLATFORM_CONFIG_DATA InputPayload; UINT8 *pBuffer = NULL; UINT32 Offset = 0; UINT32 PcdSize = 0; BOOLEAN LargePayloadAvailable = FALSE; NVDIMM_ENTRY(); // Don't support using this function to retrieve PCD OEM Config data. // Use FwCmdGetPcdSmallPayload if (PartitionId == PCD_OEM_PARTITION_ID) { ReturnCode = EFI_UNSUPPORTED; goto Finish; } SetMem(&InputPayload, sizeof(InputPayload), 0x0); if (pDimm == NULL || ppRawData == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (PartitionId == PCD_LSA_PARTITION_ID) { PcdSize = pDimm->PcdLsaPartitionSize; } else { ReturnCode = EFI_UNSUPPORTED; goto Finish; } /* * PcdSize is 0 if Media is disabled. * PcdSize was retrieved at driver load time so it is possible that since load time there * was a fatal media error that this would not catch. We would then be returning cached data * from a media disabled DIMM instead of erroring out. * It could also be possible that FW was busy during driver load time, so disable the cache. */ if (PcdSize == 0) { gPCDCacheEnabled = 0; ReturnCode = FwCmdGetPlatformConfigDataSize(pDimm, PartitionId, &PcdSize); if (EFI_ERROR(ReturnCode) || PcdSize == 0) { NVDIMM_DBG("FW CMD Error: %d", ReturnCode); goto Finish; } else if (PartitionId == PCD_LSA_PARTITION_ID) { pDimm->PcdLsaPartitionSize = PcdSize; } } *ppRawData = AllocateZeroPool(PcdSize); if (*ppRawData == NULL) { NVDIMM_WARN("Can't allocate memory for Platform Config Data (%d bytes)", PcdSize); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } if (gPCDCacheEnabled) { if (pDimm->pPcdLsa && PartitionId == PCD_LSA_PARTITION_ID) { CopyMem_S(*ppRawData, PcdSize, pDimm->pPcdLsa, PcdSize); goto Finish; } } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } /** Retrieve the PCD/LSA data **/ pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetAdminFeatures; pFwCmd->SubOpcode = SubopPlatformDataInfo; InputPayload.PartitionId = PartitionId; InputPayload.CmdOptions.RetrieveOption = PCD_CMD_OPT_PARTITION_DATA; pFwCmd->InputPayloadSize = sizeof(InputPayload); CHECK_RESULT(IsLargePayloadAvailable(pDimm, &LargePayloadAvailable), Finish); if (!LargePayloadAvailable) { pBuffer = AllocateZeroPool(PcdSize); if (pBuffer == NULL) { NVDIMM_ERR("Can't allocate memory for PCD partition buffer (%d bytes)", PcdSize); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } /** Get PCD by small payload in loop in 128 byte chunks **/ pFwCmd->LargeOutputPayloadSize = 0; pFwCmd->OutputPayloadSize = PCD_GET_SMALL_PAYLOAD_DATA_SIZE; InputPayload.CmdOptions.PayloadType = PCD_CMD_OPT_SMALL_PAYLOAD; for (Offset = 0; Offset < PcdSize; Offset += PCD_GET_SMALL_PAYLOAD_DATA_SIZE) { InputPayload.Offset = Offset; CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), &InputPayload, pFwCmd->InputPayloadSize); #ifdef OS_BUILD ReturnCode = PassThru(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); #else ReturnCode = PassThruWithRetryOnFwAborted(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); #endif if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending Platform Config Data (Get Data) command (Offset = %d, RC = " FORMAT_EFI_STATUS ")", Offset, ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pBuffer + Offset, PcdSize - Offset, pFwCmd->OutPayload, PCD_GET_SMALL_PAYLOAD_DATA_SIZE); } #ifdef OS_BUILD gPCDCacheEnabled = 1; #endif } else { /** Get PCD by large payload in single call **/ pFwCmd->LargeOutputPayloadSize = PcdSize; InputPayload.Offset = 0; InputPayload.CmdOptions.PayloadType = PCD_CMD_OPT_LARGE_PAYLOAD; if (pFwCmd->InputPayloadSize > IN_PAYLOAD_SIZE) { NVDIMM_DBG("The size of command parameters is greater than the size of the small payload."); } CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), &InputPayload, pFwCmd->InputPayloadSize); #ifdef OS_BUILD ReturnCode = PassThru(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); #else ReturnCode = PassThruWithRetryOnFwAborted(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); #endif if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending Platform Config Data (Get Data) command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } #ifdef OS_BUILD gPCDCacheEnabled = 1; #endif } if (gPCDCacheEnabled) { VOID *pTempCache = NULL; UINTN pTempCacheSz = 0; if (PartitionId == PCD_LSA_PARTITION_ID) { pDimm->pPcdLsa = AllocateZeroPool(pDimm->PcdLsaPartitionSize); pTempCache = pDimm->pPcdLsa; pTempCacheSz = pDimm->PcdLsaPartitionSize; } if (!LargePayloadAvailable) { // Check for null first CHECK_NOT_TRUE((NULL != *ppRawData && NULL != pBuffer), Finish); CopyMem_S(*ppRawData, PcdSize, pBuffer, PcdSize); if (NULL != pTempCache) { CopyMem_S(pTempCache, pTempCacheSz, pBuffer, PcdSize); } } else { CopyMem_S(*ppRawData, PcdSize, pFwCmd->LargeOutputPayload, PcdSize); if (NULL != pTempCache) { CopyMem_S(pTempCache, pTempCacheSz, pFwCmd->LargeOutputPayload, PcdSize); } } goto Finish; } if (!LargePayloadAvailable) { CHECK_NOT_TRUE((NULL != *ppRawData && NULL != pBuffer), Finish); CopyMem_S(*ppRawData, PcdSize, pBuffer, PcdSize); } else { CopyMem_S(*ppRawData, PcdSize, pFwCmd->LargeOutputPayload, PcdSize); } Finish: FREE_POOL_SAFE(pFwCmd); FREE_POOL_SAFE(pBuffer); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get the PCD size @param[in] pDimm The target DIMM @param[in] PartitionId The partition ID of the PCD @param[out] pPcdSize Pointer to the PCD size @retval EFI_INVALID_PARAMETER Invalid parameter passed @retval EFI_OUT_OF_RESOURCES Could not allocate memory @retval EFI_SUCCESS Command successfully run **/ EFI_STATUS FwCmdGetPlatformConfigDataSize ( IN DIMM *pDimm, IN UINT8 PartitionId, OUT UINT32 *pPcdSize ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; PT_INPUT_PAYLOAD_GET_PLATFORM_CONFIG_DATA InputPayload; PT_OUTPUT_PAYLOAD_GET_PLATFORM_CONFIG_DATA_SIZE OutputPcdSize; NVDIMM_ENTRY(); if (pDimm == NULL || pPcdSize == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ZeroMem(&InputPayload, sizeof(InputPayload)); ZeroMem(&OutputPcdSize, sizeof(OutputPcdSize)); pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } /** Retrieve the PCD/LSA data **/ pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetAdminFeatures; pFwCmd->SubOpcode = SubopPlatformDataInfo; InputPayload.PartitionId = PartitionId; InputPayload.CmdOptions.RetrieveOption = PCD_CMD_OPT_PARTITION_SIZE; pFwCmd->InputPayloadSize = sizeof(InputPayload); pFwCmd->LargeOutputPayloadSize = 0; pFwCmd->OutputPayloadSize = PCD_GET_SMALL_PAYLOAD_DATA_SIZE; InputPayload.CmdOptions.PayloadType = PCD_CMD_OPT_SMALL_PAYLOAD; CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), &InputPayload, pFwCmd->InputPayloadSize); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending Platform Config Data (Get Data) command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(&OutputPcdSize, sizeof(OutputPcdSize), pFwCmd->OutPayload, PCD_GET_SMALL_PAYLOAD_DATA_SIZE); *pPcdSize = OutputPcdSize.Size; Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Validate the PCD Oem Config Header. @param[in] pOemHeader Pointer to NVDIMM Configuration Header @retval EFI_INVALID_PARAMETER NULL pointer for DIMM structure provided @retval EFI_VOLUME_CORRUPTED Header is invalid. Signature or checksum failed. @retval EFI_NO_MEDIA Size of the header exceeds allowed capacity @retval EFI_SUCCESS Success - Valid config header **/ EFI_STATUS ValidatePcdOemHeader( IN NVDIMM_CONFIGURATION_HEADER *pOemHeader) { if (NULL == pOemHeader) { return EFI_INVALID_PARAMETER; } // Check for corruption first if (pOemHeader->Header.Signature != NVDIMM_CONFIGURATION_HEADER_SIG) { NVDIMM_WARN("Incorrect signature of the DIMM Configuration Header table"); return EFI_VOLUME_CORRUPTED; } if (pOemHeader->Header.Length > PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE) { NVDIMM_WARN("Length of PCD header is greater than PCD OEM partition size"); return EFI_VOLUME_CORRUPTED; } else if (!IsChecksumValid(pOemHeader, pOemHeader->Header.Length, pOemHeader->Header.Checksum)) { NVDIMM_WARN("The DIMM Configuration table checksum is invalid."); return EFI_VOLUME_CORRUPTED; } // If there's no corruption, then everything that follows should only // be based on some incompatibility with BIOS // BIOS revision too old or too new if (IS_NVDIMM_CONFIGURATION_HEADER_REV_INVALID(pOemHeader)) { NVDIMM_WARN("Unsupported revision of the DIMM Configuration Header table"); // This should be more descriptive (like EFI_INCOMPATIBLE_VERSION) but // unfortunately anything other than EFI_VOLUME_CORRUPTED prevents // ipmctl delete -pcd from working. Too much headache to fix right now. return EFI_VOLUME_CORRUPTED; } return EFI_SUCCESS; } /** Determine if PCD Header is all zeros. @param[in] pOemHeader Pointer to NVDIMM Configuration Header @param[out] bIsZero Pointer to boolean. True if Config Header is zero. @retval EFI_INVALID_PARAMETER NULL pointer for DIMM structure provided @retval EFI_SUCCESS Success **/ EFI_STATUS IsPcdOemHeaderZero( IN NVDIMM_CONFIGURATION_HEADER *pOemHeader, OUT BOOLEAN *bIsZero) { int i = 0; if (NULL == bIsZero || NULL == pOemHeader) { return EFI_INVALID_PARAMETER; } *bIsZero = TRUE; for (i = 0; i < sizeof(NVDIMM_CONFIGURATION_HEADER); i++) { if (((UINT8*)pOemHeader)[i] != 0) { *bIsZero = FALSE; break; } } return EFI_SUCCESS; } /** Determine the total size of PCD Config Data area by finding the largest offset any of the 3 data sets. @param[in] pOemHeader Pointer to NVDIMM Configuration Header @param[out] pOemDataSize Size of the PCD Config Data @retval EFI_INVALID_PARAMETER NULL pointer for DIMM structure provided @retval EFI_SUCCESS Success **/ EFI_STATUS GetPcdOemDataSize(NVDIMM_CONFIGURATION_HEADER *pOemHeader, UINT32 *pOemDataSize) { if (NULL == pOemHeader || NULL == pOemDataSize) { return EFI_INVALID_PARAMETER; } UINT32 MaxCur = pOemHeader->CurrentConfStartOffset + pOemHeader->CurrentConfDataSize; UINT32 MaxIn = pOemHeader->ConfInputStartOffset + pOemHeader->ConfInputDataSize; UINT32 MaxOut = pOemHeader->ConfOutputStartOffset + pOemHeader->ConfOutputDataSize; // At least return the size of the header... *pOemDataSize = MAX(sizeof(NVDIMM_CONFIGURATION_HEADER), MAX(MaxOut, MAX(MaxCur, MaxIn))); NVDIMM_DBG("GetPcdOemDataSize. MaxOemDataSize: %d.\n", *pOemDataSize); // Prevent any crazy large values... if (*pOemDataSize > PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE) { NVDIMM_DBG("GetPcdOemDataSize. MaxOemDataSize is unexpectedly LARGE: %d.\n", *pOemDataSize); return EFI_VOLUME_CORRUPTED; } return EFI_SUCCESS; } /** Retrieve Pcd data using small payload method only. Data is retrieved in 128 byte chunks. @param[in] pDimm The DIMM to retrieve security info on @param[in] PartitionId The partition ID of the PCD @param[in] Offset Offset of data to be read from PCD region @param[in,out] pData Pointer to a buffer used to retrieve PCD data. Must be at least 128 bytes. @param[in] DataSize Size of the pData buffer in bytes. @retval EFI_INVALID_PARAMETER NULL pointer for DIMM structure provided @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval EFI_... Other errors from subroutines @retval EFI_SUCCESS Success **/ EFI_STATUS FwCmdGetPcdSmallPayload( IN DIMM *pDimm, IN UINT8 PartitionId, IN UINT32 Offset, IN OUT UINT8 *pData, IN UINT8 DataSize ) { NVM_FW_CMD *pFwCmd = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; PT_INPUT_PAYLOAD_GET_PLATFORM_CONFIG_DATA *pInputPayload = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || pData == NULL) { goto Finish; } // Don't let try to read outside PCD or buffer if (((Offset + PCD_GET_SMALL_PAYLOAD_DATA_SIZE) > PCD_PARTITION_SIZE) || (DataSize > PCD_GET_SMALL_PAYLOAD_DATA_SIZE) || (0 == DataSize)) { goto Finish; } /* * PcdSize is 0 if Media is disabled or FW is busy. * PcdSize was retrieved at driver load time so it is possible that since load time there * was a fatal media error that this would not catch. We would then be returning cached data * from a media disabled DIMM instead of erroring out. * It could also be possible that FW was busy during driver load time, so disable the cache. */ if (gPCDCacheEnabled && pDimm->PcdOemPartitionSize == 0) { gPCDCacheEnabled = 0; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pInputPayload = (PT_INPUT_PAYLOAD_GET_PLATFORM_CONFIG_DATA*) pFwCmd->InputPayload; pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetAdminFeatures; pFwCmd->SubOpcode = SubopPlatformDataInfo; pFwCmd->InputPayloadSize = sizeof(*pInputPayload); pFwCmd->LargeOutputPayloadSize = 0; pFwCmd->OutputPayloadSize = PCD_GET_SMALL_PAYLOAD_DATA_SIZE; pInputPayload->PartitionId = PartitionId; pInputPayload->CmdOptions.RetrieveOption = PCD_CMD_OPT_PARTITION_DATA; pInputPayload->CmdOptions.PayloadType = PCD_CMD_OPT_SMALL_PAYLOAD; pInputPayload->Offset = Offset; #ifdef OS_BUILD ReturnCode = PassThru(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); #else ReturnCode = PassThruWithRetryOnFwAborted(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); #endif if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending Platform Config Data (Get Data) command (Offset = %d, RC = " FORMAT_EFI_STATUS ")", Offset, ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pData, DataSize, pFwCmd->OutPayload, DataSize); Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command get Platform Config Data via small payload only. For OEM Config Data, small payload via ASL is faster than large payload via SMM. Execute a FW command to get information about DIMM regions and REGIONs configuration. The caller is responsible for a memory deallocation of the ppRawData @param[in] pDimm The Intel NVM Dimm to retrieve identity info on @param[out] ppRawData Pointer to a new buffer pointer for storing retrieved data @param[out] pRawDataSize Pointer to size of the data retrieved. @retval EFI_SUCCESS: Success @retval EFI_OUT_OF_RESOURCES: memory allocation failure @retval EFI_NO_MEDIA: PCD Partition size reported as 0. Can't read data. @retval EFI_NOT_FOUND: No valid PCD Config Data Header. Maybe all zero's. @retval EFI_VOLUME_CORRUPTED: PCD Config Data header is invalid/corrupted **/ EFI_STATUS GetPcdOemConfigDataUsingSmallPayload( IN DIMM *pDimm, OUT UINT8 **ppRawData, OUT UINT32 *pRawDataSize ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT8 *pBuffer = NULL; UINT32 Offset = 0; UINT8 TmpBuf[PCD_GET_SMALL_PAYLOAD_DATA_SIZE]; NVDIMM_ENTRY(); if (pDimm == NULL || ppRawData == NULL || pRawDataSize == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Disable the cache when media is disabled or when the fw is busy if (gPCDCacheEnabled && pDimm->PcdOemPartitionSize == 0) { gPCDCacheEnabled = 0; } // Return the cached data if (gPCDCacheEnabled && pDimm->pPcdOem) { *ppRawData = AllocateZeroPool(pDimm->PcdOemSize); if (*ppRawData == NULL) { NVDIMM_WARN("Can't allocate memory for Platform Config Data (%d bytes)", pDimm->PcdOemSize); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } CopyMem_S(*ppRawData, pDimm->PcdOemSize, pDimm->pPcdOem, pDimm->PcdOemSize); *pRawDataSize = pDimm->PcdOemSize; goto Finish; } // Read first block which includes config header ReturnCode = FwCmdGetPcdSmallPayload(pDimm, PCD_OEM_PARTITION_ID, 0, TmpBuf, sizeof(TmpBuf)); if (EFI_ERROR(ReturnCode)) { goto Finish; } // Validate the Header NVDIMM_CONFIGURATION_HEADER *pOemHeader = (NVDIMM_CONFIGURATION_HEADER*) TmpBuf; ReturnCode = ValidatePcdOemHeader(pOemHeader); if (EFI_ERROR(ReturnCode)) { BOOLEAN IsZero = TRUE; EFI_STATUS tmpRc = IsPcdOemHeaderZero(pOemHeader, &IsZero); if ((EFI_SUCCESS == tmpRc) && (TRUE == IsZero)) { ReturnCode = EFI_NOT_FOUND; } goto Finish; } // Get size of OEM Config Data UINT32 OemDataSize = 0; /*Instead of making one more Passthru call to get the PCD size, get it from the OemHeader*/ ReturnCode = GetPcdOemDataSize(pOemHeader, &OemDataSize); if (EFI_ERROR(ReturnCode)) { goto Finish; } // Ensure buffer size is rounded up to next PCD_GET_SMALL_PAYLOAD_DATA_SIZE boundary UINT32 BufferSize = ((OemDataSize / PCD_GET_SMALL_PAYLOAD_DATA_SIZE) + 1) * PCD_GET_SMALL_PAYLOAD_DATA_SIZE; pDimm->PcdOemPartitionSize = OemDataSize; pBuffer = AllocateZeroPool(BufferSize); if (pBuffer == NULL) { NVDIMM_ERR("Can't allocate memory for PCD partition buffer (%d bytes)", BufferSize); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } // Save the first 128 bytes already read CopyMem_S(pBuffer, BufferSize, TmpBuf, PCD_GET_SMALL_PAYLOAD_DATA_SIZE); /** Get PCD by small payload in loop in 128 byte chunks **/ for (Offset = PCD_GET_SMALL_PAYLOAD_DATA_SIZE; Offset < OemDataSize; Offset += PCD_GET_SMALL_PAYLOAD_DATA_SIZE) { ReturnCode = FwCmdGetPcdSmallPayload(pDimm, PCD_OEM_PARTITION_ID, Offset, pBuffer + Offset, (UINT8)PCD_GET_SMALL_PAYLOAD_DATA_SIZE); if (EFI_ERROR(ReturnCode)) { goto Finish; } } if ( gPCDCacheEnabled && OemDataSize > 0) { VOID *pTempCache = NULL; // Save data cache info pDimm->PcdOemSize = OemDataSize; pDimm->pPcdOem = AllocateZeroPool(PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE); pTempCache = pDimm->pPcdOem; if ((NULL != pTempCache) && (OemDataSize <= PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE)) { CopyMem_S(pTempCache, PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE, pBuffer, OemDataSize); } } //Assign new data to the requester data pointer *ppRawData = pBuffer; *pRawDataSize = OemDataSize; Finish: if (EFI_ERROR(ReturnCode)) { // If error, free the buffer FREE_POOL_SAFE(pBuffer); if(NULL != ppRawData) *ppRawData = NULL; } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command access/write Platform Config Data using small payload only. The function allows to specify the requested data offset and the size. The buffer's minimal size is the size of the Partition! The offset and the data size needs to be aligned to the SET_SMALL_PAYLOAD_DATA_SIZE which is 64 bytes. @param[in] pDimm The Intel NVM Dimm to send Platform Config Data to @param[in] PartitionId Partition number for data to be send to @param[in] pRawData Pointer to a data buffer that will be sent to the DIMM @param[in] ReqOffset Data write starting point @param[in] ReqDataSize Number of bytes to write @retval EFI_SUCCESS: Success, otherwise: Error **/ EFI_STATUS FwSetPCDFromOffsetSmallPayload( IN DIMM *pDimm, IN UINT8 PartitionId, IN UINT8 *pRawData, IN UINT32 ReqOffset, IN UINT32 ReqDataSize) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; PT_INPUT_PAYLOAD_SET_DATA_PLATFORM_CONFIG_DATA InPayloadSetData; UINT32 StartingPageOffset = ((ReqOffset / PCD_SET_SMALL_PAYLOAD_DATA_SIZE)*PCD_SET_SMALL_PAYLOAD_DATA_SIZE); UINT32 WriteOffset = 0; SetMem(&InPayloadSetData, sizeof(InPayloadSetData), 0x0); if ((pDimm == NULL) || (pRawData == NULL) || ((ReqOffset+ReqDataSize) > PCD_PARTITION_SIZE) || (0 == ReqDataSize) || (ReqOffset % PCD_SET_SMALL_PAYLOAD_DATA_SIZE) || (ReqDataSize % PCD_SET_SMALL_PAYLOAD_DATA_SIZE)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (PartitionId == PCD_OEM_PARTITION_ID) { // Using small payload transactions. // Only allow up to 64kb to protect upper 64kb for OEM data. if ((ReqOffset + ReqDataSize) > PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE) { ReturnCode = EFI_BUFFER_TOO_SMALL; goto Finish; } // If partition size is 0, then prevent write if (0 == pDimm->PcdOemPartitionSize) { ReturnCode = EFI_BAD_BUFFER_SIZE; goto Finish; } } else if (PartitionId == PCD_LSA_PARTITION_ID) { // If partition size is 0, then prevent write if (0 == pDimm->PcdLsaPartitionSize) { ReturnCode = EFI_BAD_BUFFER_SIZE; goto Finish; } } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } /** Set the Platform Config Data **/ pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtSetAdminFeatures; pFwCmd->SubOpcode = SubopPlatformDataInfo; InPayloadSetData.PartitionId = PartitionId; pFwCmd->InputPayloadSize = sizeof(InPayloadSetData); /** Set PCD by small payload in loop in 64 byte chunks **/ InPayloadSetData.PayloadType = PCD_CMD_OPT_SMALL_PAYLOAD; pFwCmd->LargeInputPayloadSize = 0; for (WriteOffset = StartingPageOffset; WriteOffset < (ReqOffset+ReqDataSize); WriteOffset += PCD_SET_SMALL_PAYLOAD_DATA_SIZE) { InPayloadSetData.Offset = WriteOffset; CopyMem_S(InPayloadSetData.Data, sizeof(InPayloadSetData.Data), pRawData + (WriteOffset - StartingPageOffset), PCD_SET_SMALL_PAYLOAD_DATA_SIZE); CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), &InPayloadSetData, pFwCmd->InputPayloadSize); pFwCmd->OutputPayloadSize = 0; ReturnCode = PassThru(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending Platform Config Data (Offset=%d ReturnCode=" FORMAT_EFI_STATUS ", FWStatus=%d)", WriteOffset, ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } } Finish: FREE_POOL_SAFE(pFwCmd); return ReturnCode; } /** Firmware command set Platform Config Data. Execute a FW command to send REGIONs configuration to the Platform Config Data. @param[in] pDimm The Intel NVM Dimm to send Platform Config Data to @param[in] PartitionId Partition number for data to be send to @param[in] pRawData Pointer to a data buffer that will be sent to the DIMM @param[in] RawDataSize Size of pRawData in bytes @retval EFI_SUCCESS: Success @retval EFI_OUT_OF_RESOURCES: memory allocation failure **/ EFI_STATUS FwCmdSetPlatformConfigData ( IN DIMM *pDimm, IN UINT8 PartitionId, IN UINT8 *pRawData, IN UINT32 RawDataSize ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; PT_INPUT_PAYLOAD_SET_DATA_PLATFORM_CONFIG_DATA InPayloadSetData; UINT8 *pPartition = NULL; UINT32 Offset = 0; UINT32 PcdSize = 0; VOID *pTempCache = NULL; UINTN pTempCacheSz = 0; UINT8 *pOEMPartitionData = NULL; BOOLEAN LargePayloadAvailable = FALSE; NVDIMM_ENTRY(); SetMem(&InPayloadSetData, sizeof(InPayloadSetData), 0x0); if ((pDimm == NULL) || (pRawData == NULL) || (RawDataSize > PCD_PARTITION_SIZE) || (0 == RawDataSize)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (PartitionId == PCD_OEM_PARTITION_ID) { // Using small payload transactions. // Only allow up to 64kb to protect upper 64kb for OEM data. if (RawDataSize > PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gPCDCacheEnabled) { if (NULL == pDimm->pPcdOem) { pDimm->pPcdOem = AllocateZeroPool(PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE); } pDimm->PcdOemSize = RawDataSize; pTempCache = pDimm->pPcdOem; pTempCacheSz = PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE; } // If partition size is 0, then prevent write if (0 == pDimm->PcdOemPartitionSize) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } else if (PartitionId == PCD_LSA_PARTITION_ID) { if (gPCDCacheEnabled) { if (NULL == pDimm->pPcdLsa) { pDimm->pPcdLsa = AllocateZeroPool(pDimm->PcdLsaPartitionSize); } pTempCache = pDimm->pPcdLsa; pTempCacheSz = pDimm->PcdLsaPartitionSize; } } PcdSize = RawDataSize; if (PcdSize == 0) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (RawDataSize > PcdSize) { NVDIMM_DBG("Partition's data is greater than the size of partition."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pPartition = AllocateZeroPool(PcdSize); if (pPartition == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } /** Copy the data to 128KB partition. If the data is smaller, the rest of partition will be empty (filled with 0) **/ CopyMem_S(pPartition, PcdSize, pRawData, RawDataSize); /** Set the Platform Config Data **/ pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtSetAdminFeatures; pFwCmd->SubOpcode = SubopPlatformDataInfo; InPayloadSetData.PartitionId = PartitionId; pFwCmd->InputPayloadSize = sizeof(InPayloadSetData); if (pFwCmd->InputPayloadSize > IN_PAYLOAD_SIZE) { NVDIMM_DBG("Size of command parameters is greater than the size of the small payload."); } CHECK_RESULT(IsLargePayloadAvailable(pDimm, &LargePayloadAvailable), Finish); if (!LargePayloadAvailable) { /** Set PCD by small payload in loop in 64 byte chunks **/ InPayloadSetData.PayloadType = PCD_CMD_OPT_SMALL_PAYLOAD; pFwCmd->LargeInputPayloadSize = 0; for (Offset = 0; Offset < PcdSize; Offset += PCD_SET_SMALL_PAYLOAD_DATA_SIZE) { InPayloadSetData.Offset = Offset; CopyMem_S(InPayloadSetData.Data, sizeof(InPayloadSetData.Data), pPartition + Offset, MIN(PcdSize - Offset, PCD_SET_SMALL_PAYLOAD_DATA_SIZE)); CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), &InPayloadSetData, pFwCmd->InputPayloadSize); pFwCmd->OutputPayloadSize = 0; ReturnCode = PassThru(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending Platform Config Data (Offset=%d ReturnCode=" FORMAT_EFI_STATUS ", FWStatus=%d)", Offset, ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } else if (gPCDCacheEnabled){ if (pTempCache) { CopyMem_S((INT8*)(pTempCache) + Offset, pTempCacheSz - Offset, InPayloadSetData.Data, PCD_SET_SMALL_PAYLOAD_DATA_SIZE); } } } } else { // If it is OEM_PARTITION_ID we need to read entire // partition, copy over OEM Data and write // back entire partition if (PartitionId == PCD_OEM_PARTITION_ID) { CHECK_RESULT(FwCmdGetPcdLargePayload(pDimm, PCD_OEM_PARTITION_ID, &pOEMPartitionData), Finish); CopyMem_S(pFwCmd->LargeInputPayload + PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE, sizeof(pFwCmd->LargeInputPayload)- PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE, pOEMPartitionData + PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE, PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE); pFwCmd->LargeInputPayloadSize = PCD_PARTITION_SIZE; } else { pFwCmd->LargeInputPayloadSize = PcdSize; } /** Set PCD by large payload in single call **/ InPayloadSetData.Offset = 0; InPayloadSetData.PayloadType = PCD_CMD_OPT_LARGE_PAYLOAD; CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), &InPayloadSetData, pFwCmd->InputPayloadSize); /** Save 128KB partition to Large Payload **/ CopyMem_S(pFwCmd->LargeInputPayload, sizeof(pFwCmd->LargeInputPayload), pPartition, PcdSize); #ifdef OS_BUILD ReturnCode = PassThru(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); #else ReturnCode = PassThruWithRetryOnFwAborted(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); #endif if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error detected when sending Platform Config Data (ReturnCode=" FORMAT_EFI_STATUS ", FWStatus=%d)", ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); } else if (gPCDCacheEnabled) { if (pTempCache) { CopyMem_S(pTempCache, pTempCacheSz, pPartition, PcdSize); } } } Finish: FREE_POOL_SAFE(pPartition); FREE_POOL_SAFE(pFwCmd); FREE_POOL_SAFE(pOEMPartitionData); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get Alarm Thresholds @param[in] pDimm The Intel NVM Dimm to retrieve Alarm Thresholds @param[out] ppPayloadAlarmThresholds Area to place the Alarm Thresholds data returned from FW. The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadAlarmThresholds is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetAlarmThresholds ( IN DIMM *pDimm, OUT PT_PAYLOAD_ALARM_THRESHOLDS **ppPayloadAlarmThresholds ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || ppPayloadAlarmThresholds == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetFeatures; pFwCmd->SubOpcode = SubopAlarmThresholds; pFwCmd->OutputPayloadSize = sizeof(PT_PAYLOAD_ALARM_THRESHOLDS); *ppPayloadAlarmThresholds = AllocateZeroPool(sizeof(**ppPayloadAlarmThresholds)); if (*ppPayloadAlarmThresholds == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto FinishAfterFwCmdAlloc; } ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error detected when sending AlarmThresholds command (RC = " FORMAT_EFI_STATUS ", Status = %d)", ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto FinishAfterPayloadAlloc; } CopyMem_S(*ppPayloadAlarmThresholds, sizeof(**ppPayloadAlarmThresholds), pFwCmd->OutPayload, sizeof(**ppPayloadAlarmThresholds)); FinishAfterPayloadAlloc: if (EFI_ERROR(ReturnCode)){ FREE_POOL_SAFE(*ppPayloadAlarmThresholds); } FinishAfterFwCmdAlloc: FREE_POOL_SAFE(pFwCmd); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to set Alarm Thresholds @param[in] pDimm The Intel NVM Dimm to set Alarm Thresholds @param[in] ppPayloadAlarmThresholds Alarm Thresholds data to set @param[out] pFwReturnCode @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadAlarmThresholds is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdSetAlarmThresholds ( IN DIMM *pDimm, IN PT_PAYLOAD_ALARM_THRESHOLDS *pPayloadAlarmThresholds ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || pPayloadAlarmThresholds == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtSetFeatures; pFwCmd->SubOpcode = SubopAlarmThresholds; pFwCmd->InputPayloadSize = sizeof(PT_PAYLOAD_ALARM_THRESHOLDS); CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), pPayloadAlarmThresholds, pFwCmd->InputPayloadSize); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error detected when sending AlarmThresholds command (RC = " FORMAT_EFI_STATUS ", Status = %d)", ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto FinishAfterFwCmdAlloc; } FinishAfterFwCmdAlloc: FreePool(pFwCmd); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Runs and handles errors errors for firmware update over both large and small payloads. @param[in] pDimm Pointer to DIMM @param[in] pImageBuffer Pointer to fw image buffer @param[in] ImageBufferSize Size in bytes of fw image buffer @param[out] pNvmStatus Pointer to Nvm status variable to set on error @param[out] pCommandStatus OPTIONAL structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdUpdateFw( IN DIMM *pDimm, IN CONST VOID *pImageBuffer, IN UINTN ImageBufferSize, OUT NVM_STATUS *pNvmStatus, OUT COMMAND_STATUS *pCommandStatus OPTIONAL ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; FW_SMALL_PAYLOAD_UPDATE_PACKET *pInputPayload = NULL; UINT64 ChunkSize = 0; UINT64 BytesToCopy = 0; UINT64 BytesWrittenTotal = 0; UINT16 PacketOffset = 0; UINT8 CurrentRetryCount = 0; UINT8 *InputPayloadBuffer = NULL; UINT8 ArsStatus = 0; UINT8 Percent = 0; BOOLEAN LargePayloadAvailable = FALSE; BOOLEAN RetryDueToARS = FALSE; if (NULL == pDimm || NULL == pImageBuffer || NULL == pNvmStatus) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } CHECK_RESULT_MALLOC(pFwCmd, AllocateZeroPool(sizeof(*pFwCmd)), Finish); pFwCmd->Opcode = PtUpdateFw; //!< Firmware update category pFwCmd->SubOpcode = SubopUpdateFw; //!< Execute the firmware image pFwCmd->InputPayloadSize = sizeof(*pInputPayload); pInputPayload = (FW_SMALL_PAYLOAD_UPDATE_PACKET *) &pFwCmd->InputPayload; // Limited number of bytes in small payload packet CHECK_RESULT(IsLargePayloadAvailable(pDimm, &LargePayloadAvailable), Finish); if (!LargePayloadAvailable) { ChunkSize = UPDATE_FIRMWARE_SMALL_PAYLOAD_DATA_PACKET_SIZE; pInputPayload->PayloadTypeSelector = FW_UPDATE_SMALL_PAYLOAD_SELECTOR; InputPayloadBuffer = pInputPayload->Data; } else { ChunkSize = ImageBufferSize; pInputPayload->PayloadTypeSelector = FW_UPDATE_LARGE_PAYLOAD_SELECTOR; pFwCmd->LargeInputPayloadSize = (UINT32)ImageBufferSize; InputPayloadBuffer = pFwCmd->LargeInputPayload; } // Send new firmware image in chunks PacketOffset = 0; BytesWrittenTotal = 0; BytesToCopy = ChunkSize; // Large payload will only execute the loop once (one big chunk) // and only call INIT_TRANSFER. // Small payload will call all of INIT, CONTINUE, and END TRANSFER // during chunking. while (BytesWrittenTotal < ImageBufferSize) { Percent = (UINT8)((BytesWrittenTotal*100)/ImageBufferSize); if (NULL != pCommandStatus) { SetObjProgress(pCommandStatus, pDimm->DeviceHandle.AsUint32, Percent); } pInputPayload->PacketNumber = PacketOffset; if (BytesWrittenTotal == 0) { // Needs to run at the beginning pInputPayload->TransactionType = FW_UPDATE_INIT_TRANSFER; } else if (BytesWrittenTotal < ImageBufferSize - BytesToCopy) { // Should run in the middle. Won't occur for large payload pInputPayload->TransactionType = FW_UPDATE_CONTINUE_TRANSFER; } else { // Runs at end for small payload only, it includes final chunk. // Not needed for large payload pInputPayload->TransactionType = FW_UPDATE_END_TRANSFER; } // The chunk size won't change for small payload (fw image size was already // enforced to be a multiple of 64 bytes), but it potentially could for // large payload at some point. BytesToCopy = MIN(ImageBufferSize - BytesWrittenTotal, ChunkSize); NVDIMM_DBG("BytesToCopy: %d %d / %d. TT: 0x%x", BytesToCopy, BytesWrittenTotal, ImageBufferSize, pInputPayload->TransactionType); CopyMem_S(InputPayloadBuffer, BytesToCopy, (UINT8 *)pImageBuffer + BytesWrittenTotal, BytesToCopy); ReturnCode = PassThru(pDimm, pFwCmd, PT_UPDATEFW_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { // Try to cancel Address Range Scrub (ARS) if it is in progress // on the DCPMM (and is returning FW_DEVICE_BUSY as a result). // However for Purley BIOS, it can intercept a failed fw update command // due to ARS, so it will return DSM_RETRY_SUGGESTED. We should honor // that as well and retry after cancelling ARS. RetryDueToARS = pFwCmd->Status == FW_DEVICE_BUSY; #ifdef OS_BUILD RetryDueToARS |= pFwCmd->DsmStatus == DSM_RETRY_SUGGESTED; #endif if (RetryDueToARS) { if (++CurrentRetryCount >= MAX_FW_UPDATE_RETRY_ON_DEV_BUSY) { *pNvmStatus = NVM_ERR_BUSY_DEVICE; ReturnCode = EFI_ABORTED; goto Finish; } // If there's an issue getting ARS information or canceling ARS // we don't need to abort because of it. At least two scenarios: // * Can't cancel ARS for some reason. Retry MAX_FW_UPDATE_RETRY_ON_DEV_BUSY times, it's fine // * Cancel ARS fails because ARS just completed naturally in between // receiving IN_PROGRESS and sending disable ARS (Linux kernel can restart ARS // if cancelled). Also not an issue. Continue. CHECK_RESULT_CONTINUE(FwCmdGetARS(pDimm, &ArsStatus)); if (LONG_OP_STATUS_IN_PROGRESS == ArsStatus) { NVDIMM_DBG("ARS in progress. Disabling ARS.\n"); CHECK_RESULT_CONTINUE(FwCmdDisableARS(pDimm)); } // Retry current packet continue; } else if (pFwCmd->Status == FW_UPDATE_ALREADY_OCCURRED) { NVDIMM_DBG("FW Update failed, FW already occurred\n"); *pNvmStatus = NVM_ERR_FIRMWARE_ALREADY_LOADED; goto Finish; } else { *pNvmStatus = NVM_ERR_OPERATION_FAILED; goto Finish; } } PacketOffset++; BytesWrittenTotal += BytesToCopy; } pDimm->RebootNeeded = TRUE; *pNvmStatus = NVM_SUCCESS_FW_RESET_REQUIRED; ReturnCode = EFI_SUCCESS; Finish: if (NULL != pCommandStatus && NULL != pDimm) { ClearNvmStatus(GetObjectStatus(pCommandStatus, pDimm->DeviceHandle.AsUint32), NVM_OPERATION_IN_PROGRESS); } FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get debug logs size in MB @param[in] pDimm Target DIMM structure pointer @param[out] pLogSizeInMb - number of MB of Logs to be fetched @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetFwDebugLogSize( IN DIMM *pDimm, OUT UINT64 *pLogSizeInMb ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVM_FW_CMD *pFwCmd = NULL; PT_OUTPUT_PAYLOAD_FW_DEBUG_LOG *pDbgSmallOutPayload = NULL; PT_INPUT_PAYLOAD_FW_DEBUG_LOG *pInputPayload = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || pLogSizeInMb == NULL) { goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetLog; pFwCmd->SubOpcode = SubopFwDbg; pFwCmd->InputPayloadSize = sizeof(PT_INPUT_PAYLOAD_FW_DEBUG_LOG); pFwCmd->OutputPayloadSize = sizeof(PT_OUTPUT_PAYLOAD_FW_DEBUG_LOG); pInputPayload = (PT_INPUT_PAYLOAD_FW_DEBUG_LOG *) &pFwCmd->InputPayload; pInputPayload->LogAction = ActionRetrieveDbgLogSize; ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get FW debug log size"); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } pDbgSmallOutPayload = (PT_OUTPUT_PAYLOAD_FW_DEBUG_LOG *)pFwCmd->OutPayload; *pLogSizeInMb = pDbgSmallOutPayload->LogSize; Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get a specified debug log @param[in] pDimm Target DIMM structure pointer @param[in] LogSource Debug log source buffer to retrieve @param[out] ppDebugLogBuffer - an allocated buffer containing the raw debug logs @param[out] pDebugLogBufferSize - the size of the raw debug log buffer @param[out] pCommandStatus structure containing detailed NVM error codes Note: The caller is responsible for freeing the returned buffers @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetFwDebugLog ( IN DIMM *pDimm, IN UINT8 LogSource, OUT VOID **ppDebugLogBuffer, OUT UINTN *pDebugLogBufferSize, OUT COMMAND_STATUS *pCommandStatus ) { NVM_FW_CMD *pFwCmd = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 LogPageOffset = 0; UINT64 CurrentDebugLogSizeInMbs = 0; UINT64 LogSizeBytesToFetch = 0; PT_INPUT_PAYLOAD_FW_DEBUG_LOG *pInputPayload = NULL; UINT64 ChunkSize = 0; UINT64 BytesToCopy = 0; UINT64 BytesReadTotal = 0; UINT8 LogAction = 0; UINT8 *OutputPayload = NULL; BOOLEAN LargePayloadAvailable = FALSE; NVDIMM_ENTRY(); if (pDimm == NULL || ppDebugLogBuffer == NULL || pDebugLogBufferSize == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } // Populate log size bytes to fetch switch (LogSource) { case FW_DEBUG_LOG_SOURCE_MEDIA: LogAction = ActionGetDbgLogPage; ReturnCode = FwCmdGetFwDebugLogSize(pDimm, &CurrentDebugLogSizeInMbs); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_SECURITY_VIOLATION) { SetObjStatusForDimm(pCommandStatus, pDimm, NVM_ERR_INVALID_SECURITY_STATE); } else { SetObjStatusForDimm(pCommandStatus, pDimm, NVM_ERR_FW_DBG_LOG_FAILED_TO_GET_SIZE); } goto Finish; } LogSizeBytesToFetch = MIB_TO_BYTES(CurrentDebugLogSizeInMbs); break; case FW_DEBUG_LOG_SOURCE_SRAM: LogAction = ActionGetSramLogPage; LogSizeBytesToFetch = SRAM_LOG_PAGE_SIZE_BYTES; break; case FW_DEBUG_LOG_SOURCE_SPI: LogAction = ActionGetSpiLogPage; LogSizeBytesToFetch = SPI_LOG_PAGE_SIZE_BYTES; break; default: ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (LogSizeBytesToFetch == 0) { SetObjStatusForDimm(pCommandStatus, pDimm, NVM_INFO_FW_DBG_LOG_NO_LOGS_TO_FETCH); ReturnCode = EFI_NOT_STARTED; goto Finish; } *ppDebugLogBuffer = AllocateZeroPool(LogSizeBytesToFetch); if (*ppDebugLogBuffer == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->Opcode = PtGetLog; pFwCmd->SubOpcode = SubopFwDbg; pFwCmd->InputPayloadSize = sizeof(*pInputPayload); pInputPayload = (PT_INPUT_PAYLOAD_FW_DEBUG_LOG *) &pFwCmd->InputPayload; pInputPayload->LogAction = LogAction; // Default for DDRT large payload transactions. 128 bytes for smbus CHECK_RESULT(IsLargePayloadAvailable(pDimm, &LargePayloadAvailable), Finish); if (!LargePayloadAvailable) { ChunkSize = SMALL_PAYLOAD_SIZE; OutputPayload = pFwCmd->OutPayload; pInputPayload->PayloadType = DEBUG_LOG_PAYLOAD_TYPE_SMALL; pFwCmd->OutputPayloadSize = SMALL_PAYLOAD_SIZE; pFwCmd->LargeOutputPayloadSize = 0; } else { ChunkSize = MIB_TO_BYTES(1); OutputPayload = pFwCmd->LargeOutputPayload; pInputPayload->PayloadType = DEBUG_LOG_PAYLOAD_TYPE_LARGE; pFwCmd->OutputPayloadSize = 0; pFwCmd->LargeOutputPayloadSize = OUT_MB_SIZE; } /** Fetch whole buffer, iterate by chunk size **/ LogPageOffset = 0; BytesReadTotal = 0; while (BytesReadTotal < LogSizeBytesToFetch) { pInputPayload->LogPageOffset = LogPageOffset; ReturnCode = PassThru(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get firmware debug log, LogPageOffset = %d\n", LogPageOffset); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } BytesToCopy = MIN(LogSizeBytesToFetch - BytesReadTotal, ChunkSize); CopyMem_S((UINT8 *)*ppDebugLogBuffer + BytesReadTotal, BytesToCopy, OutputPayload, BytesToCopy); LogPageOffset++; BytesReadTotal += BytesToCopy; } *(pDebugLogBufferSize) = BytesReadTotal; Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get Error logs Small and large payloads are optional, but at least one has to be provided. @param[in] pDimm Target DIMM structure pointer @param[in] pInputPayload - filled input payload @param[out] pOutputPayload - small payload result data of get error log operation @param[in] OutputPayloadSize - size of small payload @param[out] pLargeOutputPayload - large payload result data of get error log operation @param[in] LargeOutputPayloadSize - size of large payload @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetErrorLog ( IN DIMM *pDimm, IN PT_INPUT_PAYLOAD_GET_ERROR_LOG *pInputPayload, OUT VOID *pOutputPayload OPTIONAL, IN UINT32 OutputPayloadSize OPTIONAL, OUT VOID *pLargeOutputPayload OPTIONAL, IN UINT32 LargeOutputPayloadSize OPTIONAL ) { NVM_FW_CMD *pFwCmd = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pInputPayload == NULL || (pOutputPayload == NULL && pLargeOutputPayload == NULL)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetLog; pFwCmd->SubOpcode = SubopErrorLog; pFwCmd->InputPayloadSize = sizeof(*pInputPayload); pFwCmd->OutputPayloadSize = OutputPayloadSize; pFwCmd->LargeOutputPayloadSize = LargeOutputPayloadSize; CopyMem_S(&pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), pInputPayload, pFwCmd->InputPayloadSize); ReturnCode = PassThru(pDimm, pFwCmd, PT_LONG_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get error log\n"); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } if (pOutputPayload != NULL && OutputPayloadSize > 0) { CopyMem_S(pOutputPayload, OutputPayloadSize, &pFwCmd->OutPayload, OutputPayloadSize); } if (pLargeOutputPayload != NULL && LargeOutputPayloadSize > 0) { CopyMem_S(pLargeOutputPayload, LargeOutputPayloadSize, &pFwCmd->LargeOutputPayload, LargeOutputPayloadSize); } Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get number of Command Effect Log Entries @param[in] pDimm Target DIMM structure pointer @param[out] pCount number of command effect log entries @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetCommandEffectLogCount( IN DIMM *pDimm, OUT UINT32 *pCount ) { NVM_FW_CMD *pFwCmd = NULL; PT_INPUT_PAYLOAD_GET_COMMAND_EFFECT_LOG InputPayload; PT_OUTPUT_PAYLOAD_GET_CEL_COUNT OutputPayload; ZeroMem(&InputPayload, sizeof(InputPayload)); ZeroMem(&OutputPayload, sizeof(OutputPayload)); EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pDimm == NULL || pCount == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } CHECK_RESULT_MALLOC(pFwCmd, AllocateZeroPool(sizeof(*pFwCmd)), Finish); // Get number of CEL entries InputPayload.PayloadType = SmallPayload; InputPayload.LogAction = EntriesCount; InputPayload.EntryOffset = 0; pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetLog; pFwCmd->SubOpcode = SubopCommandEffectLog; pFwCmd->InputPayloadSize = sizeof(InputPayload); pFwCmd->OutputPayloadSize = sizeof(OutputPayload); pFwCmd->LargeOutputPayloadSize = 0; CopyMem_S(&pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), &InputPayload, sizeof(InputPayload)); // TODO: Standardize and clean this up ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending Command Effect Log size command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } *pCount = ((PT_OUTPUT_PAYLOAD_GET_CEL_COUNT *)(pFwCmd->OutPayload))->LogEntryCount; Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get Command Effect Log Entries @param[in] pDimm Target DIMM structure pointer @param[out] ppLogEntry A pointer to the CEL entry table for a given PMem module. @param[out] pEntryCount The number of CEL entries @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetCommandEffectLog( IN DIMM *pDimm, OUT COMMAND_EFFECT_LOG_ENTRY **ppLogEntry, OUT UINT32 *pEntryCount ) { NVM_FW_CMD *pFwCmd = NULL; VOID * pBuffer = NULL; BOOLEAN LargePayloadAvailable = FALSE; PT_INPUT_PAYLOAD_GET_COMMAND_EFFECT_LOG InputPayload; PT_OUTPUT_PAYLOAD_GET_CEL_ENTRIES OutputPayload; ZeroMem(&InputPayload, sizeof(InputPayload)); ZeroMem(&OutputPayload, sizeof(OutputPayload)); const UINT8 MaxCelEntriesPerSmallPayload = (sizeof(PT_OUTPUT_PAYLOAD_GET_COMMAND_EFFECT_LOG) / sizeof(COMMAND_EFFECT_LOG_ENTRY)); UINT8 CelEntriesToCopy = 0; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pDimm == NULL || ppLogEntry == NULL || pEntryCount == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } CHECK_RESULT_MALLOC(pFwCmd, AllocateZeroPool(sizeof(*pFwCmd)), Finish); // Get command effect log count. Not necessary for large payload, but keeping // for easy code and it's a cheap call CHECK_RESULT(FwCmdGetCommandEffectLogCount(pDimm, pEntryCount), Finish); // Allocate final command effect log array CHECK_RESULT_MALLOC(*ppLogEntry, AllocateZeroPool((*pEntryCount)*sizeof(COMMAND_EFFECT_LOG_ENTRY)), Finish); // Set up input payload InputPayload.LogAction = CelEntries; InputPayload.EntryOffset = 0; CHECK_RESULT(IsLargePayloadAvailable(pDimm, &LargePayloadAvailable), Finish); if (LargePayloadAvailable) { InputPayload.PayloadType = LargePayload; } else { InputPayload.PayloadType = SmallPayload; } // Set up pFwCmd struct pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetLog; pFwCmd->SubOpcode = SubopCommandEffectLog; pFwCmd->InputPayloadSize = sizeof(InputPayload); pFwCmd->OutputPayloadSize = sizeof(OutputPayload); if (LargePayloadAvailable) { pFwCmd->LargeOutputPayloadSize = (UINT32)((*pEntryCount)*sizeof(COMMAND_EFFECT_LOG_ENTRY)); } else { pFwCmd->LargeOutputPayloadSize = 0; } while (InputPayload.EntryOffset < *pEntryCount) { // Copy updated InputPayload into pFwCmd CopyMem_S(&pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), &InputPayload, sizeof(InputPayload)); // TODO: Standardize and clean this up along with other fw commands ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending Command Effect Log command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } if (LargePayloadAvailable) { CelEntriesToCopy = (UINT8)*pEntryCount; pBuffer = pFwCmd->LargeOutputPayload; } else { CelEntriesToCopy = MIN((UINT8)*pEntryCount - InputPayload.EntryOffset, MaxCelEntriesPerSmallPayload); pBuffer = pFwCmd->OutPayload; } CopyMem_S((*ppLogEntry) + InputPayload.EntryOffset, CelEntriesToCopy * sizeof(COMMAND_EFFECT_LOG_ENTRY), pBuffer, CelEntriesToCopy * sizeof(COMMAND_EFFECT_LOG_ENTRY)); InputPayload.EntryOffset += CelEntriesToCopy; } Finish: if (EFI_ERROR(ReturnCode) && ppLogEntry != NULL && *ppLogEntry != NULL) { FREE_POOL_SAFE(*ppLogEntry); } FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get SMART and Health Info @param[in] pDimm The Intel NVM Dimm to retrieve SMART and Health Info @param[out] ppPayloadSmartAndHealth Area to place SMART and Health Info data The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadSmartAndHealth is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetSmartAndHealth ( IN DIMM *pDimm, OUT PT_PAYLOAD_SMART_AND_HEALTH **ppPayloadSmartAndHealth ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || ppPayloadSmartAndHealth == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetLog; pFwCmd->SubOpcode = SubopSmartHealth; pFwCmd->OutputPayloadSize = sizeof(**ppPayloadSmartAndHealth); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error detected when sending SmartAndHealth command (RC = " FORMAT_EFI_STATUS ", Status = %d)", ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } *ppPayloadSmartAndHealth = AllocateZeroPool(sizeof(**ppPayloadSmartAndHealth)); if (*ppPayloadSmartAndHealth == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } CopyMem_S(*ppPayloadSmartAndHealth, sizeof(**ppPayloadSmartAndHealth), pFwCmd->OutPayload, sizeof(**ppPayloadSmartAndHealth)); Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Command to send a pass-through firmware command to retrieve a specified memory info page @param[in] pDimm Dimm to retrieve the specified memory info page from @param[in] PageNum The specific memory info page @param[in] PageSize The size of memory info page, which is 128 bytes @param[out] ppPayloadMemoryInfoPage Area to place the retrieved memory info page contents The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadMediaErrorsInfo is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetMemoryInfoPage ( IN DIMM *pDimm, IN CONST UINT8 PageNum, IN CONST UINT32 PageSize, OUT VOID **ppPayloadMemoryInfoPage ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; PT_INPUT_PAYLOAD_MEMORY_INFO InputPayload; NVDIMM_ENTRY(); SetMem(&InputPayload, sizeof(InputPayload), 0x0); if (pDimm == NULL || ppPayloadMemoryInfoPage == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (MEMORY_INFO_PAGE_4 == PageNum && pDimm->FwVer.FwApiMajor < 2) { ReturnCode = EFI_UNSUPPORTED; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } InputPayload.MemoryPage = PageNum; pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetLog; pFwCmd->SubOpcode = SubopMemInfo; pFwCmd->InputPayloadSize = sizeof(InputPayload); pFwCmd->OutputPayloadSize = PageSize; CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), &InputPayload, pFwCmd->InputPayloadSize); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error detected when sending MemoryInfoPage command (RC = " FORMAT_EFI_STATUS ", Status = %d)", ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto FinishAfterFwCmdAlloc; } *ppPayloadMemoryInfoPage = AllocateZeroPool(PageSize); if (*ppPayloadMemoryInfoPage == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto FinishAfterFwCmdAlloc; } CopyMem_S(*ppPayloadMemoryInfoPage, PageSize, pFwCmd->OutPayload, pFwCmd->OutputPayloadSize); FinishAfterFwCmdAlloc: FREE_POOL_SAFE(pFwCmd); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get Firmware Image Info @param[in] pDimm Dimm to retrieve Firmware Image Info for @param[out] ppPayloadFwImage Area to place Firmware Image Info data The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadFwImage is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetFirmwareImageInfo ( IN DIMM *pDimm, OUT PT_PAYLOAD_FW_IMAGE_INFO **ppPayloadFwImage ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || ppPayloadFwImage == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetLog; pFwCmd->SubOpcode = SubopFwImageInfo; pFwCmd->OutputPayloadSize = sizeof(**ppPayloadFwImage); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error detected when sending FirmwareImageInfo command (RC = " FORMAT_EFI_STATUS ", Status = %d)", ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto FinishError; } *ppPayloadFwImage = AllocateZeroPool(sizeof(**ppPayloadFwImage)); if (*ppPayloadFwImage == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto FinishError; } CopyMem_S(*ppPayloadFwImage, sizeof(**ppPayloadFwImage), pFwCmd->OutPayload, sizeof(**ppPayloadFwImage)); FinishError: FREE_POOL_SAFE(pFwCmd); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get Power Management Policy Info (for FIS 1.3+) @param[in] pDimm The Intel Persistent Memory Module to retrieve Power Management Policy Info @param[out] ppPayloadPowerManagementPolicy Area to place Power Management Policy Info data The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadPowerManagementPolicy is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetPowerManagementPolicy( IN DIMM *pDimm, OUT PT_POWER_MANAGEMENT_POLICY_OUT **ppPayloadPowerManagementPolicy ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || ppPayloadPowerManagementPolicy == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } *ppPayloadPowerManagementPolicy = AllocateZeroPool(sizeof(**ppPayloadPowerManagementPolicy)); if (NULL == *ppPayloadPowerManagementPolicy) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetFeatures; pFwCmd->SubOpcode = SubopPolicyPowMgmt; pFwCmd->OutputPayloadSize = sizeof((*ppPayloadPowerManagementPolicy)->Payload); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error detected when sending PowerManagementPolicy command (RC = " FORMAT_EFI_STATUS ", Status = %d)", ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S((*ppPayloadPowerManagementPolicy)->Payload.Data, sizeof((*ppPayloadPowerManagementPolicy)->Payload), pFwCmd->OutPayload, sizeof((*ppPayloadPowerManagementPolicy)->Payload)); (*ppPayloadPowerManagementPolicy)->FisMajor = pDimm->FwVer.FwApiMajor; (*ppPayloadPowerManagementPolicy)->FisMinor = pDimm->FwVer.FwApiMinor; ReturnCode = EFI_SUCCESS; Finish: if (EFI_ERROR(ReturnCode) && (NULL != ppPayloadPowerManagementPolicy)) { FREE_POOL_SAFE(*ppPayloadPowerManagementPolicy); } FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #ifdef OS_BUILD /** Firmware command to get PMON Info @param[in] pDimm The Intel Persistent Memory Module to retrieve PMON Info @param[out] pPayloadPMONRegisters Area to place PMON Registers data The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or pPayloadPMONRegisters is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetPMONRegisters( IN DIMM *pDimm, IN UINT8 SmartDataMask, OUT PMON_REGISTERS *pPayloadPMONRegisters ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || pPayloadPMONRegisters == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetFeatures; pFwCmd->SubOpcode = SubopPMONRegisters; pFwCmd->InputPayload[0] = SmartDataMask; pFwCmd->InputPayloadSize = sizeof(SmartDataMask); pFwCmd->OutputPayloadSize = sizeof(*pPayloadPMONRegisters); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error detected when sending PMONRegisters command (RC = " FORMAT_EFI_STATUS ", Status = %d)", ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pPayloadPMONRegisters, sizeof(*pPayloadPMONRegisters), pFwCmd->OutPayload, sizeof(*pPayloadPMONRegisters)); Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to set PMON Info @param[in] pDimm The Intel Persistent Memory Module to retrieve PMON Info @param[out] PMONGroupEnable Specifies which PMON Group to enable. The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdSetPMONRegisters( IN DIMM *pDimm, IN UINT8 PMONGroupEnable ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); /** ...Valid PMON groups -0xA -0xF **/ if (pDimm == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtSetFeatures; pFwCmd->SubOpcode = SubopPMONRegisters; pFwCmd->InputPayload[0] = PMONGroupEnable; pFwCmd->InputPayloadSize = sizeof(PMONGroupEnable); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error detected when sending PMONRegisters command (RC = " FORMAT_EFI_STATUS ", Status = %d)", ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif /** Firmware command to get package sparing policy @param[in] pDimm The Intel NVM Dimm to retrieve Package Sparing policy @param[out] ppPayloadPackageSparingPolicy Area to place Package Sparing policy data The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadPackageSparingPolicy is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetPackageSparingPolicy ( IN DIMM *pDimm, OUT PT_PAYLOAD_GET_PACKAGE_SPARING_POLICY **ppPayloadPackageSparingPolicy ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || ppPayloadPackageSparingPolicy == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } CHECK_RESULT_MALLOC(*ppPayloadPackageSparingPolicy, AllocateZeroPool(sizeof(**ppPayloadPackageSparingPolicy)), Finish); // Package sparing is disabled for Intel Optane 300 series, so exit // out beforehand because it's been dropped from the firmware interface // entirely (and will return an error) if (pDimm->FwVer.FwApiMajor == 3) { (*ppPayloadPackageSparingPolicy)->Enable = FALSE; (*ppPayloadPackageSparingPolicy)->Supported = FALSE; ReturnCode = EFI_SUCCESS; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetFeatures; pFwCmd->SubOpcode = SubopPolicyPackageSparing; pFwCmd->OutputPayloadSize = sizeof(**ppPayloadPackageSparingPolicy); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error detected when sending GetPackageSparingPolicy command (RC = " FORMAT_EFI_STATUS ", Status = %d)", ReturnCode, pFwCmd->Status); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(*ppPayloadPackageSparingPolicy, sizeof(**ppPayloadPackageSparingPolicy), pFwCmd->OutPayload, sizeof(**ppPayloadPackageSparingPolicy)); Finish: FREE_POOL_SAFE(pFwCmd) NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get long operation status FW command @param[in] pDimm Dimm to retrieve long operation status from @param[out] pFwStatus FW status returned by dimm. @param[out] pLongOpStatus Filled payload with data @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS FwCmdGetLongOperationStatus( IN DIMM *pDimm, OUT UINT8 *pFwStatus, OUT PT_OUTPUT_PAYLOAD_FW_LONG_OP_STATUS *pLongOpStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || pFwStatus == NULL || pLongOpStatus == NULL) { goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetLog; pFwCmd->SubOpcode = SubopLongOperationStat; pFwCmd->OutputPayloadSize = sizeof(*pLongOpStatus); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); *pFwStatus = pFwCmd->Status; if (EFI_ERROR(ReturnCode)) { /** FW_INTERNAL_DEVICE_ERROR or FW_DATA_NOT_SET occurs when there is no long operation at this moment. Which one depends on FIS**/ if (!(pDimm->FwVer.FwApiMajor == 1 && pDimm->FwVer.FwApiMinor <= 4 && pFwCmd->Status == FW_INTERNAL_DEVICE_ERROR) && pFwCmd->Status != FW_DATA_NOT_SET) { NVDIMM_WARN("Error detected when sending LongOperationStatus command (RC = " FORMAT_EFI_STATUS ", Status = %d)", ReturnCode, pFwCmd->Status); } FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pLongOpStatus, sizeof(*pLongOpStatus), pFwCmd->OutPayload, sizeof(*pLongOpStatus)); ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Free memory for a single block window Frees the resources held by a block window @param[out] bw: The block window to free **/ VOID FreeBlockWindow( OUT BLOCK_WINDOW *pBw ) { NVDIMM_ENTRY(); if (pBw != NULL) { FREE_POOL_SAFE(pBw->ppBwApt); } FREE_POOL_SAFE(pBw); NVDIMM_EXIT(); } /** Assign spa address to a given mailbox or block window field. @param Rdpa Device Region Physical Address to convert @param pNvDimmRegionTable The NVDIMM region that helps describe this region of memory @param pIntTbl Interleave table @param ppField mailbox or block window field to assign to @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER when pIntTbl has 0 values in fields line_size or lines_described **/ EFI_STATUS AssignSpaAddress( IN UINT64 Rdpa, IN NvDimmRegionMappingStructure *pNvDimmRegionTable, IN SpaRangeTbl *pSpaRangeTable, IN InterleaveStruct *pIntTbl OPTIONAL, OUT VOID **ppField ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT64 SpaAddr = 0; if (pNvDimmRegionTable == NULL || pSpaRangeTable == NULL) { goto Finish; } ReturnCode = RdpaToSpa(Rdpa, pNvDimmRegionTable, pSpaRangeTable, pIntTbl, &SpaAddr); if (!EFI_ERROR(ReturnCode)) { *ppField = (VOID *) SpaAddr; } Finish: return ReturnCode; } static UINT16 ParseFwBuild( IN UINT8 Mbs, IN UINT8 Lsb ) { return (BCD_TO_TWO_DEC(Mbs) * 100) + BCD_TO_TWO_DEC(Lsb); } /** Parse Firmware Version Parse the FW version returned by the FW into a CPU format FW Payload has the FW version encoded in a binary coded decimal format @param[in] Fwr - Firmware revision in BCD format @retval Parsed firmware version as friendly FIRMWARE_VERSION structure **/ FIRMWARE_VERSION ParseFwVersion( IN UINT8 Fwr[FW_BCD_VERSION_LEN] ) { FIRMWARE_VERSION FwVer; NVDIMM_ENTRY(); ZeroMem(&FwVer, sizeof(FwVer)); if (Fwr == NULL) { goto Finish; } FwVer.FwProduct = BCD_TO_TWO_DEC(Fwr[FWR_PRODUCT_VERSION_OFFSET]); FwVer.FwRevision = BCD_TO_TWO_DEC(Fwr[FWR_REVISION_VERSION_OFFSET]); FwVer.FwSecurityVersion = BCD_TO_TWO_DEC(Fwr[FWR_SECURITY_VERSION_OFFSET]); FwVer.FwBuild = ParseFwBuild(Fwr[FWR_BUILD_VERSION_HI_OFFSET], Fwr[FWR_BUILD_VERSION_LOW_OFFSET]); Finish: NVDIMM_EXIT(); return FwVer; } /** Parse the BCD formatted FW API version into major and minor @param[out] pDimm @param[in] pPayload **/ VOID ParseFwApiVersion( OUT DIMM *pDimm, IN PT_ID_DIMM_PAYLOAD *pPayload ) { NVM_API_VERSION ApiVersion; NVDIMM_ENTRY(); ZeroMem(&ApiVersion, sizeof(ApiVersion)); ApiVersion.Version = pPayload->ApiVer; pDimm->FwVer.FwApiMajor = BCD_TO_TWO_DEC(ApiVersion.Byte.Digit1); pDimm->FwVer.FwApiMinor = BCD_TO_TWO_DEC(ApiVersion.Byte.Digit2); ZeroMem(&ApiVersion, sizeof(ApiVersion)); ApiVersion.Version = pPayload->ActiveApiVer; pDimm->FwActiveApiVersionMajor = BCD_TO_TWO_DEC(ApiVersion.Byte.Digit1); pDimm->FwActiveApiVersionMinor = BCD_TO_TWO_DEC(ApiVersion.Byte.Digit2); NVDIMM_EXIT(); } /** This function performs a DIMM information refresh through the DIMM Information FV command. @param[in,out] pDimm the DIMM that we want to refresh. @retval EFI_SUCCESS - the DIMM was refreshed successfully. @retval EFI_INVALID_PARAMETER - pDimm is NULL. @retval EFI_OUT_OF_RESOURCES - the memory allocation failed. **/ EFI_STATUS RefreshDimm( IN OUT DIMM *pDimm ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PT_ID_DIMM_PAYLOAD *pPayload = NULL; UINT32 Index = 0; /** @todo DE9699 Remove FIS 1.2 backwards compatibility workaround **/ UINT16 IfcExtra = 0x201; NVDIMM_ENTRY(); if (pDimm == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pPayload = AllocatePool(sizeof(PT_ID_DIMM_PAYLOAD)); if (pPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = FwCmdSmallPayload(pDimm, PtIdentifyDimm, SubopIdentify, NULL, 0, (UINT8 *)pPayload, sizeof(*pPayload)); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FW CMD Error: " FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } for (Index = 0; Index < pDimm->FmtInterfaceCodeNum; Index++) { if (pDimm->FmtInterfaceCode[Index] != pPayload->Ifc && pDimm->FmtInterfaceCode[Index] != IfcExtra) { NVDIMM_WARN("FIT and FW Interface Code mismatch"); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } } pDimm->FwVer = ParseFwVersion(pPayload->Fwr); ParseFwApiVersion(pDimm, pPayload); Finish: if (pPayload != NULL) { FreePool(pPayload); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Create and configure block window Create the block window structure. This includes locating each part of the block window in the system physical address space, and mapping each part into the virtual address space. @param[in, out] pDimm: DIMM to create the Bw for @param[in] PFitHead: Parsed Fit Head @param[in] pMbITbl: the interleave table for mailbox @param[in] pBwITbl: the interleave table for block window @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES in case of allocate memory error **/ EFI_STATUS EFIAPI CreateBw( IN OUT DIMM *pDimm, IN ParsedFitHeader *pFitHead, IN InterleaveStruct *pMbITbl OPTIONAL, IN InterleaveStruct *pBwITbl OPTIONAL ) { BLOCK_WINDOW *pBw = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; ControlRegionTbl *pControlRegTbl = NULL; BWRegionTbl *pBlockDataWindowTable = NULL; UINT32 Index = 0; UINT64 SpaAddr = 0; NVDIMM_ENTRY(); if (pDimm == NULL || pDimm->pBlockDataRegionMappingStructure == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pBw = (BLOCK_WINDOW *)AllocateZeroPool(sizeof(*pBw)); if (pBw == NULL) { NVDIMM_WARN("Unable to allocate block windows memory"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } // Getting Control Region Table with all needed BW values ReturnCode = GetControlRegionTableForNvDimmRegionTable(pFitHead, pDimm->pBlockDataRegionMappingStructure, &pControlRegTbl); if (pControlRegTbl == NULL || EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Unable to get Control region table. Returned: " FORMAT_EFI_STATUS "", ReturnCode); ReturnCode = EFI_ABORTED; goto Finish; } // Getting Block Data Windows Region Description table to get the misc offsets ReturnCode = GetBlockDataWindowRegDescTabl(pFitHead, pControlRegTbl, &pBlockDataWindowTable); if (pBlockDataWindowTable == NULL || EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Unable to get Block Data Window table. Returned: " FORMAT_EFI_STATUS "", ReturnCode); ReturnCode = EFI_ABORTED; goto Finish; } /** Control Register **/ ReturnCode = AssignSpaAddress(pControlRegTbl->CommandRegisterOffsetInBlockControlWindow, pDimm->pRegionMappingStructure, pDimm->pCtrlSpaTbl, pMbITbl, (VOID *) &(pBw->pBwCmd)); if (EFI_ERROR(ReturnCode)) { goto Finish; } NVDIMM_DBG("BW Command address = %p", pBw->pBwCmd); /** BW Status **/ ReturnCode = AssignSpaAddress(pControlRegTbl->StatusRegisterOffsetInBlockControlWindow, pDimm->pRegionMappingStructure, pDimm->pCtrlSpaTbl, pMbITbl, (VOID *) &(pBw->pBwStatus)); if (EFI_ERROR(ReturnCode)) { goto Finish; } NVDIMM_DBG("BW Status address = %p", pBw->pBwStatus); /** Aperture **/ if (pBwITbl != NULL) { pBw->LineSizeOfApt = pBwITbl->LineSize; pBw->NumSegmentsOfApt = BW_APERTURE_LENGTH / pBwITbl->LineSize; } else { pBw->LineSizeOfApt = BW_APERTURE_LENGTH; pBw->NumSegmentsOfApt = 1; } pBw->ppBwApt = (volatile VOID **) AllocateZeroPool(pBw->NumSegmentsOfApt * sizeof(*pBw->ppBwApt)); if (pBw->ppBwApt == NULL) { NVDIMM_WARN("Unable to allocate aperture in block window"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } for (Index = 0; Index < pBw->NumSegmentsOfApt; Index++) { ReturnCode = RdpaToSpa(pBlockDataWindowTable->BlockDataWindowStartLogicalOffset + (Index * pBw->LineSizeOfApt), pDimm->pBlockDataRegionMappingStructure, pDimm->pBlockDataSpaTbl, pBwITbl, &SpaAddr); pBw->ppBwApt[Index] = (VOID *) SpaAddr; if (EFI_ERROR(ReturnCode)) { goto Finish; } } NVDIMM_DBG("First interleaved BW Aperture address = %p", pBw->ppBwApt[0]); pDimm->pBw = pBw; Finish: if (EFI_ERROR(ReturnCode)) { FreeBlockWindow(pBw); } NVDIMM_EXIT_I(ReturnCode); return ReturnCode; } /** Set Block Window Command to read/write operation @param[in] Dpa - Memory DPA @param[in] Length - The transfer size is the number of cache lines (Cache line = 64 bytes) @param[in] BwOperation - Read/Write command @param[out] pBwCommand - 64-bit Command Register buffer BW Command & Address Register | RESERVED | CMD | SIZE | Reserved | BW ADDRESS | |64 58| 57 |56 49|48 38|37 0| |43 DPA ADDRESS 6|5 0| **/ VOID PrepareBwCommand( IN UINT64 Dpa, IN UINT8 Length, IN UINT8 BwOperation, OUT UINT64 *pCommand ) { NVDIMM_ENTRY(); if (pCommand == NULL) { return; } union { struct { UINT64 Dpa : 37; UINT64 Rsvd : 11; UINT64 WinSize : 8; UINT64 RwLock : 1; UINT64 Rsvd2 : 7; } Command; UINT64 AsUint64; } CommandTemp; CommandTemp.Command.RwLock = BwOperation; CommandTemp.Command.WinSize = Length; CommandTemp.Command.Dpa = (Dpa >> BW_DPA_RIGHT_SHIFT); *pCommand = CommandTemp.AsUint64; NVDIMM_EXIT(); } /** Check Block Input Parameters @param[in] pDimm: DIMM to check block window pointers @retval EFI_INVALID_PARAMETER if pDimm or some internal Block Window pointer is NULL (pBw, pBw->pBwCmd, pBw->pBwApt, pBw->pBwStatus) @ **/ EFI_STATUS EFIAPI CheckBlockInputParameters ( IN DIMM *pDimm ) { if (pDimm == NULL) { NVDIMM_WARN("DIMM not initialized."); return EFI_INVALID_PARAMETER; } if (pDimm->pBw == NULL) { NVDIMM_WARN("Block Window not initialized."); return EFI_INVALID_PARAMETER; } if (pDimm->pBw->pBwCmd == NULL) { NVDIMM_WARN("Block Window command register not initialized."); return EFI_INVALID_PARAMETER; } if (pDimm->pBw->ppBwApt == NULL) { NVDIMM_WARN("Block Window aperture register not initialized."); return EFI_INVALID_PARAMETER; } if (pDimm->pBw->pBwStatus == NULL) { NVDIMM_WARN("Block Window status register not initialized."); return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; } /** Poll Firmware Command Completion Poll the status register of the BW waiting for the status register complete bit to be set. @param[in] pDimm - Dimm with block window with submitted command @param[in] Timeout The timeout, in 100ns units, to use for the execution of the BW command. A Timeout value of 0 means that this function will wait infinitely for the command to execute. If Timeout is greater than zero, then this function will return EFI_TIMEOUT if the time required to execute the receive data command is greater than Timeout. @param[out] pStatus returned Status from BW status register @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR FW error received @retval EFI_TIMEOUT A timeout occurred while waiting for the command to execute. **/ EFI_STATUS EFIAPI CheckBwCmdTimeout ( IN DIMM *pDimm, IN UINT64 Timeout, OUT UINT32 *pStatus ) { UINT32 ReadStatus = 0; EFI_STATUS ReturnCode = EFI_DEVICE_ERROR; NVDIMM_ENTRY(); ReturnCode = CheckBlockInputParameters(pDimm); if (EFI_ERROR(ReturnCode)) { return ReturnCode; } if (pStatus == NULL) { NVDIMM_DBG("Invalid parameter."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /** Get Command Status, save it to local variable **/ ReadStatus = *(pDimm->pBw->pBwStatus); if (ReadStatus & BW_PENDING_MASK) { NVDIMM_WARN("BW register status has pending bit lit up."); /** @todo: Waiting for Cspec that covers this case. gBS->RestoreTPL(TPL_APPLICATION); gBS->CreateEvent(EVT_TIMER, 0, NULL, NULL, TimeoutEvent); gBS->SetTimer(TimeoutEvent, TimerRelative, Timeout); gBS->WaitForEvent(1, TimeoutEvent, &TimeoutEventTmpVar); gBS->RaiseTPL(TPL_CALLBACK); **/ } *pStatus = ReadStatus; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I(ReturnCode); return ReturnCode; } /** Get command status from command status register @param[in] pDimm - pointer to DIMM with Block Window @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER if some pointer is NULL @retval other - error code matching to status register **/ EFI_STATUS EFIAPI GetBwCommandStatus ( IN DIMM *pDimm ) { UINT32 Status = 0; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = CheckBlockInputParameters(pDimm); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = CheckBwCmdTimeout(pDimm, PT_TIMEOUT_INTERVAL, &Status); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** DPA Address specified in the BW Address Register is not a valid address for the NVDIMM **/ if (Status & BW_INVALID_ADDRESS_MASK) { NVDIMM_WARN("DPA Address specified in the BW Address Register is not a valid address for the NVDIMM"); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } /** An uncorrectable error occurred upon NVDIMM access to the given BW Address **/ if (Status & BW_ACCESS_ERROR) { NVDIMM_WARN("An uncorrectable error occurred upon NVDIMM access to the given BW Address"); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } /** BW request attempts to access a locked Persistent Memory region of the NVDIMM **/ if (Status & BW_PM_ACCESS_ERROR) { NVDIMM_WARN("BW request attempted to access a locked Persistent Memory region of the NVDIMM"); ReturnCode = EFI_ACCESS_DENIED; goto Finish; } /** BW request attempts to access a locked or disabled Block Window region of the NVDIMM **/ if (Status & BW_REGION_ACCESS_ERROR) { NVDIMM_WARN("BW request attempted to access a locked or disabled Block Window region of the NVDIMM"); ReturnCode = EFI_ACCESS_DENIED; goto Finish; } Finish: NVDIMM_EXIT_I(ReturnCode); return ReturnCode; } /** Read back the BW address register and that would ensure the programming has completed. **/ VOID BlockWindowProgrammingDelay(volatile UINT64 *pBlockWindowCmdRegAddress) { volatile UINT64 DummyBuffer = 0; /** Read back the BW address register and that would ensure the programming has completed. **/ DummyBuffer = *pBlockWindowCmdRegAddress; if (DummyBuffer == 0) { NVDIMM_DBG("BW address register is zero"); } } /** Get error logs for given dimm parse it and save in common error log structure @param[in] pDimm - pointer to DIMM to get errors @param[in] ThermalError - is thermal error (if not it is media error) @param[in] HighLevel - high level if true, low level otherwise @param[in] SequenceNumber - sequence number of error to fetch in queue @param[in] MaxErrorsToSave - max number of new error entries that can be saved in output array @param[out] pErrorsFetched - number of new error entries saved in output array @param[out] pErrorLogs - output array of errors @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER if some pointer is NULL @retval other - error code matching to status register **/ EFI_STATUS GetAndParseFwErrorLogForDimm( IN DIMM *pDimm, IN CONST BOOLEAN ThermalError, IN CONST BOOLEAN HighLevel, IN CONST UINT16 SequenceNumber, IN UINT32 MaxErrorsToSave, OUT UINT32 *pErrorsFetched, OUT ERROR_LOG_INFO *pErrorLogs ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; PT_OUTPUT_PAYLOAD_GET_ERROR_LOG_THERMAL_ENTRY *pThermalLogEntry = NULL; PT_OUTPUT_PAYLOAD_GET_ERROR_LOG_MEDIA_ENTRY *pMediaLogEntry = NULL; MEDIA_ERROR_LOG_INFO *pMediaErrorInfo = NULL; THERMAL_ERROR_LOG_INFO *pThermalErrorInfo = NULL; PT_INPUT_PAYLOAD_GET_ERROR_LOG InputPayload; VOID *pLargeOutputPayload = NULL; PT_OUTPUT_PAYLOAD_GET_ERROR_LOG OutPayloadGetErrorLog; LOG_INFO_DATA_RETURN OutPayloadGetErrorLogInfoData; UINT16 ReturnCount = 0; TEMPERATURE Temperature; BOOLEAN LargePayloadAvailable = FALSE; ZeroMem(&InputPayload, sizeof(InputPayload)); ZeroMem(&OutPayloadGetErrorLog, sizeof(OutPayloadGetErrorLog)); ZeroMem(&OutPayloadGetErrorLogInfoData, sizeof(OutPayloadGetErrorLogInfoData)); ZeroMem(&Temperature, sizeof(Temperature)); if (pDimm == NULL || pErrorsFetched == NULL || pErrorLogs == NULL) { goto Finish; } pLargeOutputPayload = AllocateZeroPool(OUT_MB_SIZE); if (pLargeOutputPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } InputPayload.LogParameters.Separated.LogLevel = HighLevel ? ErrorLogHighPriority : ErrorLogLowPriority; InputPayload.LogParameters.Separated.LogType = ThermalError ? ErrorLogTypeThermal : ErrorLogTypeMedia; InputPayload.SequenceNumber = SequenceNumber; InputPayload.RequestCount = (MaxErrorsToSave >= MAX_UINT16) ? MAX_UINT16 : (UINT16) MaxErrorsToSave; CHECK_RESULT(IsLargePayloadAvailable(pDimm, &LargePayloadAvailable), Finish); if (!LargePayloadAvailable) { InputPayload.LogParameters.Separated.LogInfo = ErrorLogInfoData; ReturnCode = FwCmdGetErrorLog(pDimm, &InputPayload, &OutPayloadGetErrorLogInfoData, sizeof(OutPayloadGetErrorLogInfoData), pLargeOutputPayload, 0); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to fetch error log for Dimm %x\n", pDimm->DeviceHandle.AsUint32); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } UINT16 PayloadsProcessed = 0; InputPayload.LogParameters.Separated.LogInfo = ErrorLogInfoEntries; InputPayload.LogParameters.Separated.LogEntriesPayloadReturn = ErrorLogSmallPayload; InputPayload.SequenceNumber = OutPayloadGetErrorLogInfoData.OldestSequenceNum; UINT16 LogEntrySize = ThermalError ? sizeof(PT_OUTPUT_PAYLOAD_GET_ERROR_LOG_THERMAL_ENTRY) : sizeof(PT_OUTPUT_PAYLOAD_GET_ERROR_LOG_MEDIA_ENTRY); UINT16 SmallPayloadRawSize = 0; UINT64 LargeOutputOffset = (UINT64)pLargeOutputPayload; while (ReturnCount < OutPayloadGetErrorLogInfoData.MaxLogEntries) { ReturnCode = FwCmdGetErrorLog(pDimm, &InputPayload, &OutPayloadGetErrorLog, sizeof(OutPayloadGetErrorLog), pLargeOutputPayload, 0); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to fetch error log for Dimm %x\n", pDimm->DeviceHandle.AsUint32); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } if (0 == OutPayloadGetErrorLog.ReturnCount) { break; } SmallPayloadRawSize = (LogEntrySize * OutPayloadGetErrorLog.ReturnCount); CopyMem_S((VOID *)LargeOutputOffset, SmallPayloadRawSize, OutPayloadGetErrorLog.LogEntries, SmallPayloadRawSize); if (OUT_MB_SIZE >= LargeOutputOffset + SmallPayloadRawSize - (UINT64)pLargeOutputPayload) { LargeOutputOffset += SmallPayloadRawSize; } else { NVDIMM_WARN("Buffer limit reached while fetching error log for Dimm %x\n", pDimm->DeviceHandle.AsUint32); break; } InputPayload.SequenceNumber += OutPayloadGetErrorLog.ReturnCount; ReturnCount += OutPayloadGetErrorLog.ReturnCount; PayloadsProcessed++; } } else { InputPayload.LogParameters.Separated.LogEntriesPayloadReturn = ErrorLogLargePayload; ReturnCode = FwCmdGetErrorLog(pDimm, &InputPayload, &OutPayloadGetErrorLog, sizeof(OutPayloadGetErrorLog), pLargeOutputPayload, OUT_MB_SIZE); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to fetch error log for Dimm %x\n", pDimm->DeviceHandle.AsUint32); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } ReturnCount = OutPayloadGetErrorLog.ReturnCount; } if (ReturnCount > 0) { if (ThermalError) { pThermalLogEntry = (PT_OUTPUT_PAYLOAD_GET_ERROR_LOG_THERMAL_ENTRY *) pLargeOutputPayload; } else { pMediaLogEntry = (PT_OUTPUT_PAYLOAD_GET_ERROR_LOG_MEDIA_ENTRY *) pLargeOutputPayload; } for (Index = 0; Index < ReturnCount; ++Index) { pErrorLogs[Index].DimmID = pDimm->DimmID; if (ThermalError) { pErrorLogs[Index].ErrorType = THERMAL_ERROR; pErrorLogs[Index].SystemTimestamp = pThermalLogEntry->SystemTimestamp; pThermalErrorInfo = (THERMAL_ERROR_LOG_INFO *) &pErrorLogs[Index].OutputData; Temperature.Separated.Sign = (UINT16) pThermalLogEntry->HostReportedTempData.Separated.Sign; Temperature.Separated.TemperatureValue = (UINT16) pThermalLogEntry->HostReportedTempData.Separated.Temperature; pThermalErrorInfo->Temperature = TransformFwTempToRealValue(Temperature); pThermalErrorInfo->Reported = (UINT8)pThermalLogEntry->HostReportedTempData.Separated.Reported; pThermalErrorInfo->Type = (UINT8)pThermalLogEntry->HostReportedTempData.Separated.Type; pThermalErrorInfo->SequenceNum = pThermalLogEntry->SequenceNum; pThermalLogEntry++; } else { pErrorLogs[Index].ErrorType = MEDIA_ERROR; pErrorLogs[Index].SystemTimestamp = pMediaLogEntry->SystemTimestamp; pMediaErrorInfo = (MEDIA_ERROR_LOG_INFO *) &pErrorLogs[Index].OutputData; pMediaErrorInfo->Dpa = pMediaLogEntry->Dpa; pMediaErrorInfo->Pda = pMediaLogEntry->Pda; pMediaErrorInfo->Range = pMediaLogEntry->Range; pMediaErrorInfo->ErrorType = pMediaLogEntry->ErrorType; pMediaErrorInfo->PdaValid = pMediaLogEntry->ErrorFlags.Separated.PdaValid; pMediaErrorInfo->DpaValid = pMediaLogEntry->ErrorFlags.Separated.DpaValid; pMediaErrorInfo->Interrupt = pMediaLogEntry->ErrorFlags.Separated.Interrupt; pMediaErrorInfo->Viral = pMediaLogEntry->ErrorFlags.Separated.Viral; pMediaErrorInfo->TransactionType = pMediaLogEntry->TransactionType; pMediaErrorInfo->SequenceNum = pMediaLogEntry->SequenceNum; pMediaLogEntry++; } } } *pErrorsFetched = ReturnCount; ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pLargeOutputPayload); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Prepare and send command register command @param[in] pDimm: DIMM to read from @param[in] Length: Number of bytes to read @param[in] Index: Aperture chunk index @param[in] Offset: offset from the start of the region this mem type uses @param[in] BwCommandCode: Read or write operation. @retval EFI_ACCESS_DENIED if BW request attempts to access a locked or disabled BW or PM region @retval EFI_DEVICE_ERROR If DIMM DPA address is invalid or uncorrectable access error occurred @retval EFI_INVALID_PARAMETER If pDimm, pBuffer or some internal BW pointer is NULL @retval EFI_TIMEOUT A timeout occurred while waiting for the command to execute. @retval EFI_OUT_OF_RESOURCES in case of failed region allocation **/ VOID PrepareAndSendCommandRegisterCmd( IN DIMM *pDimm, IN UINT32 Length, IN UINT16 Index, IN UINT64 Offset, IN BW_COMMAND_CODE BwCommandCode ) { CONST UINT64 ChunkOffset = Offset + Index * BW_APERTURE_LENGTH; CONST UINT32 CacheLinesToTransfer = Length / CACHE_LINE_SIZE; UINT64 Command = 0; NVDIMM_ENTRY(); /** Prepare command register command **/ PrepareBwCommand((UINT64)ChunkOffset, (UINT8)CacheLinesToTransfer, BwCommandCode, &Command); /** Send command register command **/ *pDimm->pBw->pBwCmd = Command; if (pDimm->ControlWindowLatch) { // Assure that proper delay between accesses was made BlockWindowProgrammingDelay(pDimm->pBw->pBwCmd); } DimmWPQFlush(pDimm); AsmSfence(); NVDIMM_EXIT(); return; } #ifndef OS_BUILD /** Read a number of bytes from a DIMM @param[in] pDimm: DIMM to read from @param[in] Offset: offset from the start of the region this mem type uses @param[in] Nbytes: Number of bytes to read @param[out] pBuffer: Buffer to place bytes into @retval EFI_ACCESS_DENIED if BW request attempts to access a locked or disabled BW or PM region @retval EFI_DEVICE_ERROR If DIMM DPA address is invalid or uncorrectable access error occurred @retval EFI_INVALID_PARAMETER If pDimm, pBuffer or some internal BW pointer is NULL @retval EFI_TIMEOUT A timeout occurred while waiting for the command to execute. @retval EFI_OUT_OF_RESOURCES in case of failed region allocation **/ EFI_STATUS EFIAPI ApertureRead ( IN DIMM *pDimm, IN UINT64 Offset, IN UINT64 Nbytes, OUT CHAR8 *pBuffer ) { /** Prepare control register command **/ EFI_STATUS ReturnCode = EFI_SUCCESS; UINT16 Index = 0; UINT32 Length = BW_APERTURE_LENGTH; UINT64 NotAlignedNbytes = 0; CHAR8 *pDstChunk = NULL; CHAR8 *pAlignChunk = NULL; UINT64 NoApertureChunks = 0; UINTN AlignChunkSz = 0; NVDIMM_ENTRY(); if (pBuffer == NULL) { NVDIMM_DBG("Output buffer not initialized."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = CheckBlockInputParameters(pDimm); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** For read buffer that size is not a multiple of cache line size there is need to create temporary buffer for last chunk, aligned to cache line size. **/ if (Nbytes % CACHE_LINE_SIZE != 0) { NotAlignedNbytes = Nbytes; Nbytes += CACHE_LINE_SIZE - Nbytes % CACHE_LINE_SIZE; AlignChunkSz = Nbytes % BW_APERTURE_LENGTH; pAlignChunk = AllocatePool(AlignChunkSz); if (pAlignChunk == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } } NoApertureChunks = (Nbytes / BW_APERTURE_LENGTH); for (Index = 0; Index <= NoApertureChunks; ++Index) { if (Index == NoApertureChunks) { // Last chunk, calculate lasted bytes Length = Nbytes % BW_APERTURE_LENGTH; } PrepareAndSendCommandRegisterCmd(pDimm, Length, Index, Offset, BwRead); if (pDimm->FlushRequired) { FlushInterleavedBuffer((VOID **) pDimm->pBw->ppBwApt, pDimm->pBw->LineSizeOfApt, Length); } /** Copy buffer from aperture **/ ReturnCode = GetBwCommandStatus(pDimm); if (EFI_ERROR(ReturnCode)) { goto Finish; } DimmWPQFlush(pDimm); AsmSfence(); pDstChunk = pBuffer + Index * BW_APERTURE_LENGTH; // For last chunk use temporary, aligned buffer if (Index == NoApertureChunks && NotAlignedNbytes != 0) { ReadFromInterleavedBuffer((VOID *)pAlignChunk, AlignChunkSz,(VOID **) pDimm->pBw->ppBwApt, pDimm->pBw->LineSizeOfApt, Length); CopyMem(pDstChunk, pAlignChunk, NotAlignedNbytes % BW_APERTURE_LENGTH); } else { ReadFromInterleavedBuffer((VOID *)pDstChunk, Nbytes, (VOID **) pDimm->pBw->ppBwApt, pDimm->pBw->LineSizeOfApt, Length); } } Finish: FREE_POOL_SAFE(pAlignChunk); NVDIMM_EXIT_I(ReturnCode); return ReturnCode; } /** Write a number of bytes to a DIMM @param[out] pDimm: DIMM to write to @param[in] Offset: offset from the start of the region this mem type uses @param[in] Nbytes: Number of bytes to write (less or equal to buffer size). @param[in] pBuffer: Buffer containing data to write @retval EFI_ACCESS_DENIED if BW request attempts to access a locked or disabled BW or PM region @retval EFI_DEVICE_ERROR If DIMM DPA address is invalid or uncorrectable access error occurred @retval EFI_INVALID_PARAMETER If pDimm, pBuffer or some internal BW pointer is NULL @retval EFI_TIMEOUT A timeout occurred while waiting for the command to execute. @retval EFI_OUT_OF_RESOURCES in case of failed region allocation **/ EFI_STATUS EFIAPI ApertureWrite ( OUT DIMM *pDimm, IN UINT64 Offset, IN UINT64 Nbytes, IN CHAR8 *pBuffer ) { UINT16 Index = 0; EFI_STATUS ReturnCode = EFI_SUCCESS; UINT16 Length = BW_APERTURE_LENGTH; UINT64 NotAlignedNbytes = 0; UINT64 NoApertureChunks = 0; CHAR8 *pAlignChunk = NULL; NVDIMM_ENTRY(); if (pBuffer == NULL) { NVDIMM_DBG("Input buffer not initialized."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = CheckBlockInputParameters(pDimm); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** For write buffer that size is not a multiple of cache line size there is need to create temporary buffer for last chunk, aligned to cache line size. Additional bytes have to be zeroed. **/ if (Nbytes % CACHE_LINE_SIZE != 0) { NotAlignedNbytes = Nbytes; Nbytes += CACHE_LINE_SIZE - Nbytes % CACHE_LINE_SIZE; pAlignChunk = AllocateZeroPool(Length); if (pAlignChunk == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } } NoApertureChunks = (Nbytes / BW_APERTURE_LENGTH); for (Index = 0; Index <= NoApertureChunks; ++Index) { if (Index == NoApertureChunks) { // Last chunk, calculate lasted bytes Length = Nbytes % BW_APERTURE_LENGTH; } PrepareAndSendCommandRegisterCmd(pDimm, Length, Index, Offset, BwWrite); /** Copy buffer to aperture **/ if (Index == NoApertureChunks && NotAlignedNbytes != 0) { CopyMem(pAlignChunk, pBuffer + Index * BW_APERTURE_LENGTH, NotAlignedNbytes % BW_APERTURE_LENGTH); WriteToInterleavedBuffer((VOID *)pAlignChunk, (VOID **) pDimm->pBw->ppBwApt, pDimm->pBw->LineSizeOfApt, Length); } else { WriteToInterleavedBuffer((VOID *)(pBuffer + Index * BW_APERTURE_LENGTH), (VOID **) pDimm->pBw->ppBwApt, pDimm->pBw->LineSizeOfApt, Length); } FlushInterleavedBuffer((VOID **) pDimm->pBw->ppBwApt, pDimm->pBw->LineSizeOfApt, Length); DimmWPQFlush(pDimm); AsmSfence(); #ifdef WA_MEDIA_WRITES_DELAY gBS->Stall(WA_MEDIA_WRITES_DELAY); #endif ReturnCode = GetBwCommandStatus(pDimm); if (EFI_ERROR(ReturnCode)) { goto Finish; } } Finish: FREE_POOL_SAFE(pAlignChunk); NVDIMM_EXIT_I(ReturnCode); return ReturnCode; } #else /** Read a number of bytes from a DIMM @param[in] pDimm: DIMM to read from @param[in] Offset: offset from the start of the region this mem type uses @param[in] Nbytes: Number of bytes to read @param[out] pBuffer: Buffer to place bytes into @retval EFI_ACCESS_DENIED if BW request attempts to access a locked or disabled BW or PM region @retval EFI_DEVICE_ERROR If DIMM DPA address is invalid or uncorrectable access error occurred @retval EFI_INVALID_PARAMETER If pDimm, pBuffer or some internal BW pointer is NULL @retval EFI_TIMEOUT A timeout occurred while waiting for the command to execute. @retval EFI_OUT_OF_RESOURCES in case of failed region allocation **/ EFI_STATUS EFIAPI ApertureRead( IN DIMM *pDimm, IN UINT64 Offset, IN UINT64 Nbytes, OUT CHAR8 *pBuffer ) { return EFI_UNSUPPORTED; } /** Write a number of bytes to a DIMM @param[out] pDimm: DIMM to write to @param[in] Offset: offset from the start of the region this mem type uses @param[in] Nbytes: Number of bytes to write (less or equal to buffer size). @param[in] pBuffer: Buffer containing data to write @retval EFI_ACCESS_DENIED if BW request attempts to access a locked or disabled BW or PM region @retval EFI_DEVICE_ERROR If DIMM DPA address is invalid or uncorrectable access error occurred @retval EFI_INVALID_PARAMETER If pDimm, pBuffer or some internal BW pointer is NULL @retval EFI_TIMEOUT A timeout occurred while waiting for the command to execute. @retval EFI_OUT_OF_RESOURCES in case of failed region allocation **/ EFI_STATUS EFIAPI ApertureWrite( OUT DIMM *pDimm, IN UINT64 Offset, IN UINT64 Nbytes, IN CHAR8 *pBuffer ) { return EFI_UNSUPPORTED; } #endif //OS_BUILD /** Create DIMM Perform all functions needed for DIMM initialization this includes: setting up mailbox structure retrieving and recording security status retrieving and recording the FW version retrieving and recording partition information setting up block windows @param[in] pNewDimm: input dimm structure to populate @param[in] pFitHead: fully populated NVM Firmware Interface Table @param[in] pPmttHead: fully populated Platform Memory Topology Table @param[in] Pid: SMBIOS Dimm ID of the DIMM to create @retval EFI_SUCCESS - Success @retval EFI_OUT_OF_RESOURCES - AllocateZeroPool failure @retval EFI_DEVICE_ERROR - Other errors **/ EFI_STATUS InitializeDimm ( IN DIMM *pNewDimm, IN ParsedFitHeader *pFitHead, IN ParsedPmttHeader *pPmttHead, IN UINT16 Pid ) { EFI_STATUS ReturnCode = EFI_OUT_OF_RESOURCES; EFI_STATUS TempReturnCode = EFI_SUCCESS; UINT32 Index = 0; InterleaveStruct *pMbITbl = NULL; InterleaveStruct *pBwITbl = NULL; ControlRegionTbl *pControlRegTbl = NULL; FlushHintTbl *pFlushHintTable = NULL; PT_ID_DIMM_PAYLOAD *pPayload = NULL; PT_DIMM_PARTITION_INFO_PAYLOAD *pPartitionInfoPayload = NULL; PT_GET_SECURITY_PAYLOAD *pDimmSecurityPayload = NULL; ControlRegionTbl *pControlRegTbls[MAX_IFC_NUM]; UINT32 ControlRegTblsNum = MAX_IFC_NUM; UINT32 PcdSize = 0; ZeroMem(pControlRegTbls, sizeof(pControlRegTbls)); NVDIMM_ENTRY(); // Initialize boot status bitmask pNewDimm->BootStatusBitmask = DIMM_BOOT_STATUS_UNKNOWN; // We don't need a mailbox to talk to the dimm CHECK_RESULT(GetNvDimmRegionMappingStructureForPid(pFitHead, Pid, NULL, FALSE, 0, &pNewDimm->pRegionMappingStructure), Finish); ReturnCode = GetControlRegionTableForNvDimmRegionTable(pFitHead, pNewDimm->pRegionMappingStructure, &pControlRegTbl); if ((EFI_ERROR(ReturnCode) || (pControlRegTbl == NULL))) { NVDIMM_WARN("Unable to initialize Intel NVM Dimm. Control Region is missing in NFIT."); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } /** If we fail to get the Flush Hint Table, we ignore it and assume WPQ flush is not required **/ ReturnCode = GetFlushHintTableForNvDimmRegionTable(pFitHead, pNewDimm->pRegionMappingStructure, &pFlushHintTable); if (!EFI_ERROR(ReturnCode) && pFlushHintTable != NULL) { // Found the Flush Hint Table for (Index = 0; Index < pFlushHintTable->NumberOfFlushHintAddresses; Index++) { if (pFlushHintTable->FlushHintAddress[Index] != MAX_UINT64_VALUE) { // Entry equal to MAX_UINT64_VALUE is not valid pNewDimm->pFlushAddress = (UINT64 *)pFlushHintTable->FlushHintAddress[Index]; // Assign the first valid address /** The FlushHint Table can have more than one Flush Hint Address but we should need only one to execute a WPQ flush. **/ break; } } } InitializeDimmFieldsFromAcpiTables(pNewDimm->pRegionMappingStructure, pControlRegTbl, pPmttHead, pNewDimm); ReturnCode = GetControlRegionTablesForPID(pFitHead, Pid, pControlRegTbls, &ControlRegTblsNum); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (ControlRegTblsNum > MAX_IFC_NUM) { NVDIMM_ERR("The ControlRegTblsNum value greater than %d", MAX_IFC_NUM); ReturnCode = EFI_BUFFER_TOO_SMALL; goto Finish; } for (Index = 0; Index < ControlRegTblsNum; Index++) { pNewDimm->FmtInterfaceCode[Index] = pControlRegTbls[Index]->RegionFormatInterfaceCode; } pNewDimm->FmtInterfaceCodeNum = ControlRegTblsNum; pNewDimm->NvDimmStateFlags = pNewDimm->pRegionMappingStructure->NvDimmStateFlags; if (pNewDimm->pRegionMappingStructure->InterleaveStructureIndex != 0) { ReturnCode = GetInterleaveTable(pFitHead, pNewDimm->pRegionMappingStructure->InterleaveStructureIndex, &pMbITbl); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("No Interleave Table found for mailbox but the index exists."); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } } if (pNewDimm->pRegionMappingStructure->SpaRangeDescriptionTableIndex != 0) { ReturnCode = GetSpaRangeTable(pFitHead, pNewDimm->pRegionMappingStructure->SpaRangeDescriptionTableIndex, &pNewDimm->pCtrlSpaTbl); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("No spa range table found for mailbox but the index exists."); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } } // Populate boot status bitmask based on DDRT/SMBUS interface status CHECK_RESULT(PopulateDimmBootStatusBitmaskInterfaceBits(pNewDimm, &pNewDimm->BootStatusBitmask), Finish); if (pNewDimm->BootStatusBitmask & DIMM_BOOT_STATUS_DDRT_NOT_READY) { /** Setting as non-functional is not appropriate for only DDRT down, but needed temporarily to not create new defects until future changes are integrated. (lots of commands currently rely on this field for filtering proper/improper dimms. We'll change this going forward and hopefully remove the NonFunctional field!) **/ pNewDimm->NonFunctional = TRUE; } /** Populate some more boot status bitmask bits from BSR Retrieving BSR is optional since we do not want to skip further initialization **/ TempReturnCode = FwCmdGetBsr(pNewDimm, &pNewDimm->Bsr.AsUint64); if (TempReturnCode == EFI_SUCCESS) { PopulateDimmBootStatusBitmaskBsrBits(pNewDimm, &pNewDimm->Bsr, &pNewDimm->BootStatusBitmask); } CHECK_RESULT_MALLOC(pPayload, AllocateZeroPool(sizeof(*pPayload)), Finish); CHECK_RESULT(FwCmdSmallPayload(pNewDimm, PtIdentifyDimm, SubopIdentify, NULL, 0, (UINT8 *)pPayload, sizeof(*pPayload)), Finish); NVDIMM_DBG("IdentifyDimm data:\n"); NVDIMM_DBG("Raw Capacity (4k multiply): %d\n", pPayload->Rc); pNewDimm->FlushRequired = (pPayload->Fswr & BIT0) != 0; pNewDimm->ControlWindowLatch = (pPayload->Fswr & BIT1) != 0; /** pPayload->ReturnCode in 4KiB multiples **/ pNewDimm->RawCapacity = (UINT64)pPayload->Rc * (4 * 1024); pNewDimm->Manufacturer = pPayload->Mf; pNewDimm->SkuInformation = *((SKU_INFORMATION *)&pPayload->DimmSku); CopyMem_S(pNewDimm->PartNumber, sizeof(pNewDimm->PartNumber), pPayload->Pn, sizeof(pPayload->Pn)); pNewDimm->PartNumber[PART_NUMBER_LEN - 1] = '\0'; NVDIMM_DBG("String length is %d", AsciiStrLen(pPayload->Pn)); pNewDimm->FwVer = ParseFwVersion(pPayload->Fwr); ParseFwApiVersion(pNewDimm, pPayload); pNewDimm->ControllerRid = pPayload->Rid; // Skip initialization if PMem module is unmanageable if (!IsDimmManageable(pNewDimm)) { ReturnCode = RETURN_DEVICE_ERROR; NVDIMM_DBG("PMem module: 0x%04x is unmanageable. Skip initialization.", pNewDimm->DeviceHandle.AsUint32); goto Finish; } pPartitionInfoPayload = AllocateZeroPool(sizeof(*pPartitionInfoPayload)); if (pPartitionInfoPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = FwCmdGetDimmPartitionInfo(pNewDimm, pPartitionInfoPayload); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FW CMD Error: %d", ReturnCode); if (ReturnCode == EFI_NO_MEDIA || ReturnCode == EFI_NO_RESPONSE) { /** Return success if error from FW is Media Disabled **/ ReturnCode = EFI_SUCCESS; } else { goto Finish; } } if (pPartitionInfoPayload != NULL) { /** Capacity from Partition Info Payload in 4KiB multiples**/ pNewDimm->VolatileCapacity = (UINT64) pPartitionInfoPayload->VolatileCapacity * BLOCKSIZE_4K; pNewDimm->VolatileStart = (UINT64)pPartitionInfoPayload->VolatileStart; pNewDimm->PmCapacity = (UINT64) pPartitionInfoPayload->PersistentCapacity * BLOCKSIZE_4K; pNewDimm->PmStart = pPartitionInfoPayload->PersistentStart; } ReturnCode = GetNvDimmRegionMappingStructureForPid(pFitHead, pNewDimm->DimmID, &gSpaRangeBlockDataWindowRegionGuid, FALSE, 0, &pNewDimm->pBlockDataRegionMappingStructure); if (EFI_ERROR(ReturnCode) || pNewDimm->pBlockDataRegionMappingStructure == NULL) { NVDIMM_WARN("No NVDIMM region table found for block window on dimm: 0x%x.", pNewDimm->DeviceHandle.AsUint32); ReturnCode = EFI_SUCCESS; } else { if (pNewDimm->pBlockDataRegionMappingStructure->SpaRangeDescriptionTableIndex != 0) { ReturnCode = GetSpaRangeTable(pFitHead, pNewDimm->pBlockDataRegionMappingStructure->SpaRangeDescriptionTableIndex, &pNewDimm->pBlockDataSpaTbl); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("No spa range table found for block aperture but the index exists."); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } } } ReturnCode = FwCmdGetPlatformConfigDataSize(pNewDimm, PCD_OEM_PARTITION_ID, &PcdSize); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FW CMD Error: %d", ReturnCode); if (ReturnCode == EFI_NO_MEDIA || ReturnCode == EFI_NO_RESPONSE) { /** Return success if error from FW is Media Disabled **/ ReturnCode = EFI_SUCCESS; } else { goto Finish; } } pNewDimm->PcdOemPartitionSize = PcdSize; PcdSize = 0; ReturnCode = FwCmdGetPlatformConfigDataSize(pNewDimm, PCD_LSA_PARTITION_ID, &PcdSize); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FW CMD Error: %d", ReturnCode); if (ReturnCode == EFI_NO_MEDIA || ReturnCode == EFI_NO_RESPONSE) { /** Return success if error from FW is Media Disabled **/ ReturnCode = EFI_SUCCESS; } else { goto Finish; } } pNewDimm->PcdLsaPartitionSize = PcdSize; pNewDimm->InaccessibleVolatileCapacity = 0; pNewDimm->InaccessiblePersistentCapacity = 0; pNewDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_NO_GOAL_OR_SUCCESS; pDimmSecurityPayload = AllocateZeroPool(sizeof(*pDimmSecurityPayload)); if (pDimmSecurityPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = FwCmdGetSecurityInfo(pNewDimm, pDimmSecurityPayload); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get the security state for dimm: 0x%x.", pNewDimm->DeviceHandle.AsUint32); /** If we can't get the security state, we are still trying to manage the DIMM, We assume that the security is disabled and hopefully all of the features will work. Otherwise we will get more errors on each feature that we will try to use. **/ ReturnCode = EFI_SUCCESS; pDimmSecurityPayload->SecurityStatus.AsUint32 = 0; } pNewDimm->EncryptionEnabled = (BOOLEAN)pDimmSecurityPayload->SecurityStatus.Separated.SecurityEnabled; if (pNewDimm->pBlockDataRegionMappingStructure != NULL && pNewDimm->pBlockDataRegionMappingStructure->InterleaveStructureIndex != 0) { ReturnCode = GetInterleaveTable(pFitHead, pNewDimm->pBlockDataRegionMappingStructure->InterleaveStructureIndex, &pBwITbl); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("No Interleave Table found for block window but the index exists."); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } } Finish: FREE_POOL_SAFE(pPayload); FREE_POOL_SAFE(pPartitionInfoPayload); FREE_POOL_SAFE(pDimmSecurityPayload); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check if the DIMM containing the specified DIMM ID is manageable by our software @param[in] UINT16 Dimm ID to check @retval BOOLEAN whether or not dimm is manageable **/ BOOLEAN IsDimmIdManageable( IN UINT16 DimmID ) { DIMM *pCurDimm = NULL; LIST_ENTRY *pCurDimmNode = NULL; LIST_ENTRY *pDimms = &gNvmDimmData->PMEMDev.Dimms; BOOLEAN Manageable = FALSE; for (pCurDimmNode = GetFirstNode(pDimms); !IsNull(pDimms, pCurDimmNode); pCurDimmNode = GetNextNode(pDimms, pCurDimmNode)) { pCurDimm = DIMM_FROM_NODE(pCurDimmNode); if (DimmID == pCurDimm->DimmID) { Manageable = IsDimmManageable(pCurDimm); break; } } return Manageable; } /** Get number of manageable dimms @param[in] ppDimms the Dimms array @param[in] DimmCount the number of Dimms in ppDimms array @retval UINT16 the count of manageable dimms **/ UINT16 GetManageableDimmsCount( IN DIMM **ppDimms, IN CONST UINT16 DimmCount ) { UINT16 Index = 0; UINT16 ManageableDimmsNum = 0; //Count manageable dimms for (Index = 0; Index < DimmCount; Index++) { if (ppDimms[Index] != NULL) { if (IsDimmManageable(ppDimms[Index])) { ManageableDimmsNum++; } } } return ManageableDimmsNum; } /** Free dimm Free the memory resources associated with a DIMM @param[out] pDimm - the DIMM to free **/ VOID FreeDimm( OUT DIMM *pDimm ) { NVDIMM_ENTRY(); if (pDimm == NULL) { return; } FreeBlockWindow(pDimm->pBw); FREE_POOL_SAFE(pDimm->pPcdOem); FREE_POOL_SAFE(pDimm); NVDIMM_EXIT(); } /** Remove a DIMM Perform all functions needed for when a DIMM is to be removed from the platform. This may include things like removing memory from regions, deallocating mailboxes, deallocating block windows, and verifying that this DIMM is able to be removed from the system. @param[out] pDimm: DIMM to remove @param[in] Force: If true, forcefully remove the DIMM from the system(Very destructive) @retval EFI_SUCCESS: Success @retval EFI_INVALID_PARAMETER: No DIMM structure to remove @retval EFI_NOT_READY: if unable to remove DIMM from inventory gracefully **/ EFI_STATUS RemoveDimm( OUT DIMM *pDimm, IN INT32 Force ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pDimm == NULL) { ReturnCode = EFI_INVALID_PARAMETER; } else { /** test if DIMM is used in any volume if yes and not force then return EFI_NOT_READY if yes and force then call delete volume for each volume found to have part of the dimm **/ /** find regions that contain the dimm For interleaved regions break them up For non pm regions delete the region **/ FreeDimm(pDimm); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Flushes date from the iMC buffers to the DIMM. The flushing is made by writing to the Flush Hint addresses. If there is no Flush Hint Table for the provided DIMM, The assumption is made that WPQ flush is not supported and not required. @param[in] pDimm: DIMM to flush the data into. **/ VOID DimmWPQFlush( IN DIMM *pDimm ) { NVDIMM_ENTRY(); if (pDimm != NULL && pDimm->pFlushAddress != NULL) { *pDimm->pFlushAddress = 1; // Write any data for the flush hint address to perform the flush } NVDIMM_EXIT(); } /** Copy data from an interleaved buffer to a regular buffer. Both buffers have to be equal or greater than NumOfBytes. @param[out] pRegularBuffer output regular buffer @param[in] RegularBufferSz size of the RegularBuffer @param[in] ppInterleavedBuffer input interleaved buffer @param[in] LineSize line size of interleaved buffer @param[in] NumOfBytes number of bytes to copy **/ VOID ReadFromInterleavedBuffer( OUT VOID *pRegularBuffer, IN UINTN RegularBufferSz, IN VOID **ppInterleavedBuffer, IN UINT32 LineSize, IN UINT32 NumOfBytes ) { UINT8 *pTo = NULL; UINT32 NumOfSegments = 0; UINT32 Remain = 0; UINT32 Index = 0; NVDIMM_ENTRY(); if (pRegularBuffer == NULL || ppInterleavedBuffer == NULL || LineSize == 0) { NVDIMM_DBG("Invalid input parameter."); goto Finish; } NumOfSegments = NumOfBytes / LineSize; Remain = NumOfBytes % LineSize; pTo = pRegularBuffer; for (Index = 0; Index < NumOfSegments; Index++) { CopyMem_S(pTo, RegularBufferSz, ppInterleavedBuffer[Index], LineSize); pTo += LineSize; RegularBufferSz -= LineSize; } if (Remain > 0) { CopyMem_S(pTo, RegularBufferSz, ppInterleavedBuffer[Index], Remain); } Finish: NVDIMM_EXIT(); } /** Flush data from an interleaved buffer. The InterleavedBuffer needs to be at least NumOfBytes. @param[in] ppInterleavedBuffer input interleaved buffer @param[in] LineSize line size of interleaved buffer @param[in] NumOfBytes number of bytes to copy **/ VOID FlushInterleavedBuffer( IN VOID **ppInterleavedBuffer, IN UINT32 LineSize, IN UINT32 NumOfBytes ) { UINT32 NumOfSegments = 0; UINT32 Remain = 0; UINT32 Index = 0; UINT32 Index2 = 0; NVDIMM_ENTRY(); if (ppInterleavedBuffer == NULL || LineSize == 0) { NVDIMM_DBG("Incorrect input parameter."); goto Finish; } if (gClFlush == NULL) { NVDIMM_WARN("The CPU commands were not initialized."); goto Finish; } NumOfSegments = NumOfBytes / LineSize; Remain = NumOfBytes % LineSize; for (Index = 0; Index < NumOfSegments; Index++) { for (Index2 = 0; Index2 < ROUNDUP(LineSize, CACHE_LINE_SIZE) / CACHE_LINE_SIZE; Index2++) { gClFlush(((UINT8*)ppInterleavedBuffer[Index]) + (Index2 * CACHE_LINE_SIZE)); } } if (Remain > 0) { for (Index2 = 0; Index2 < ROUNDUP(Remain, CACHE_LINE_SIZE) / CACHE_LINE_SIZE; Index2++) { gClFlush(((UINT8*)ppInterleavedBuffer[Index]) + (Index2 * CACHE_LINE_SIZE)); } } Finish: NVDIMM_EXIT(); } /** Sets the memory of the given buffer to a particular value This function does a 8 byte copy and falls back to 1 byte if required. @param[in] Length The length of bytes of the input buffer @param[in] Value The value of the buffer locations that need to be set to @param [in out] Buffer The input buffer **/ STATIC VOID * SetMem_8 ( IN OUT VOID *Buffer, IN UINTN Length, IN UINT8 Value ) { volatile UINT8 *Pointer8 = NULL; volatile UINT64 *Pointer64 = NULL; UINT32 Value32; UINT64 Value64; if ((((UINTN)Buffer & 0x7) == 0) && (Length >= 8)) { Value32 = (Value << 24) | (Value << 16) | (Value << 8) | Value; Value64 = LShiftU64 (Value32, 32) | Value32; Pointer64 = (UINT64*) Buffer; while (Length >= 8) { *(Pointer64++) = Value64; Length -= 8; } Pointer8 = (UINT8*) Pointer64; } else { Pointer8 = (UINT8*) Buffer; } while (Length-- > 0) { *(Pointer8++) = Value; } return Buffer; } /** Copies 'Length' no of bytes from source buffer into destination buffer The function attempts to perform an 8 byte copy and falls back to 1 byte copies if required @param[in] SourceBuffer Source address @param[in] Length The length in no of bytes @param[out] DestinationBuffer Destination address **/ VOID * CopyMem_8 ( IN OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length ) { volatile UINT64 *Destination64 = NULL; volatile UINT64 *Source64 = NULL; UINT32 Alignment = 0; if (((((UINTN)DestinationBuffer) & 0x7) == 0) && ((((UINTN)SourceBuffer) & 0x7) == 0) && (Length >= 8)) { if (SourceBuffer > DestinationBuffer) { Destination64 = (UINT64 *)DestinationBuffer; Source64 = (UINT64 *)SourceBuffer; // Copy the bytes first using 8 byte copy while (Length >= 8) { *(Destination64++) = *(Source64++); Length -= 8; } // Copy the remaining bytes using a 1 byte copy if (Length > 0) { return CopyMem_S((UINT8 *)Destination64, Length, (UINT8 *)Source64, Length); } } else if (SourceBuffer < DestinationBuffer) { Destination64 = (UINT64*)((UINTN)DestinationBuffer + Length); Source64 = (UINT64*)((UINTN)SourceBuffer + Length); Alignment = Length & 0x7; if (Alignment != 0) { // Copy the unaligned bytes using a byte copy CopyMem_S((UINT8 *)Destination64, Alignment, (UINT8 *)Source64, Alignment); } Length -= Alignment; // Copy the remaining bytes using 8 byte copy as it is now a multiple of 8. while (Length > 0) { *(--Destination64) = *(--Source64); Length -= 8; } } } else { // Source or destination address are not aligned OR the length of bytes is less than 8 // Let's fall back to 1 byte memcopy return CopyMem_S(DestinationBuffer, Length, SourceBuffer, Length); } return DestinationBuffer; } /** Copy data from a regular buffer to an interleaved buffer. Both buffers have to be equal or greater than NumOfBytes. @param[in] pRegularBuffer input regular buffer @param[out] ppInterleavedBuffer output interleaved buffer @param[in] LineSize line size of interleaved buffer @param[in] NumOfBytes number of bytes to copy **/ VOID WriteToInterleavedBuffer( IN VOID *pRegularBuffer, OUT VOID **ppInterleavedBuffer, IN UINT32 LineSize, IN UINT32 NumOfBytes ) { UINT8 *pFrom = NULL; UINT32 NumOfSegments = 0; UINT32 Remain = 0; UINT32 Index = 0; NVDIMM_ENTRY(); if (pRegularBuffer == NULL || ppInterleavedBuffer == NULL || LineSize == 0) { NVDIMM_DBG("Invalid input parameter."); return; } NumOfSegments = NumOfBytes / LineSize; Remain = NumOfBytes % LineSize; pFrom = pRegularBuffer; for (Index = 0; Index < NumOfSegments; Index++) { CopyMem_8(ppInterleavedBuffer[Index], pFrom, LineSize); pFrom += LineSize; } if (Remain > 0) { CopyMem_8(ppInterleavedBuffer[Index], pFrom, Remain); } NVDIMM_EXIT(); } /** Clear a part or whole of interleaved buffer. @param[out] ppInterleavedBuffer interleaved buffer to clear @param[in] LineSize line size of interleaved buffer @param[in] NumOfBytes number of bytes to clear **/ VOID ClearInterleavedBuffer( OUT VOID **ppInterleavedBuffer, IN UINT32 LineSize, IN UINT32 NumOfBytes ) { UINT32 Index = 0; UINT32 NumOfSegments = 0; UINT32 Remain = 0; NVDIMM_ENTRY(); if (ppInterleavedBuffer == NULL || LineSize == 0) { NVDIMM_DBG("Invalid input parameter."); return; } NumOfSegments = NumOfBytes / LineSize; Remain = NumOfBytes % LineSize; for (Index = 0; Index < NumOfSegments; Index++) { SetMem_8(ppInterleavedBuffer[Index], LineSize, 0); } if (Remain > 0) { SetMem_8(ppInterleavedBuffer[Index], Remain, 0); } NVDIMM_EXIT(); } STATIC UINT16 GetLogEntriesCount( IN LOG_INFO_DATA_RETURN *pLogInfoDataReturn ) { INT32 Tmp = 0; UINT16 Result = 0; Tmp = (INT32) pLogInfoDataReturn->CurrentSequenceNum - (INT32) pLogInfoDataReturn->OldestSequenceNum; if (Tmp > 0) { Result = (UINT16) Tmp + 1; } else if (Tmp < 0) { Result = (UINT16) (Tmp + pLogInfoDataReturn->MaxLogEntries + 1); } else { Result = 0; } return Result; } /** Get count of media and/or thermal errors on given DIMM @param[in] pDimm - pointer to DIMM to get registers for. @param[out] pMediaLogCount - number of media errors on DIMM @param[out] pThermalLogCount - number of thermal errors on DIMM @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS FwCmdGetErrorCount( IN DIMM *pDimm, OUT UINT32 *pMediaLogCount OPTIONAL, OUT UINT32 *pThermalLogCount OPTIONAL ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PT_INPUT_PAYLOAD_GET_ERROR_LOG InputPayload; LOG_INFO_DATA_RETURN OutputPayload; NVDIMM_ENTRY(); ZeroMem(&InputPayload, sizeof(InputPayload)); ZeroMem(&OutputPayload, sizeof(OutputPayload)); if (pDimm == NULL || (pMediaLogCount == NULL && pThermalLogCount == NULL)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } InputPayload.LogParameters.Separated.LogInfo = 1; if (pMediaLogCount != NULL) { InputPayload.LogParameters.Separated.LogType = ErrorLogTypeMedia; InputPayload.LogParameters.Separated.LogLevel = 0; ReturnCode = FwCmdGetErrorLog(pDimm, &InputPayload, &OutputPayload, sizeof(OutputPayload), NULL, 0); if (EFI_ERROR(ReturnCode)) { *pMediaLogCount = 0; goto Finish; } *pMediaLogCount = GetLogEntriesCount(&OutputPayload); InputPayload.LogParameters.Separated.LogLevel = 1; ReturnCode = FwCmdGetErrorLog(pDimm, &InputPayload, &OutputPayload, sizeof(OutputPayload), NULL, 0); if (EFI_ERROR(ReturnCode)) { *pMediaLogCount = 0; goto Finish; } *pMediaLogCount += GetLogEntriesCount(&OutputPayload); } if (pThermalLogCount != NULL) { InputPayload.LogParameters.Separated.LogType = ErrorLogTypeThermal; InputPayload.LogParameters.Separated.LogLevel = 0; ReturnCode = FwCmdGetErrorLog(pDimm, &InputPayload, &OutputPayload, sizeof(OutputPayload), NULL, 0); if (EFI_ERROR(ReturnCode)) { *pThermalLogCount = 0; goto Finish; } *pThermalLogCount = GetLogEntriesCount(&OutputPayload); InputPayload.LogParameters.Separated.LogLevel = 1; ReturnCode = FwCmdGetErrorLog(pDimm, &InputPayload, &OutputPayload, sizeof(OutputPayload), NULL, 0); if (EFI_ERROR(ReturnCode)) { *pThermalLogCount = 0; goto Finish; } *pThermalLogCount += GetLogEntriesCount(&OutputPayload); } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Generate the OEM PCD Header @param[in out] pPlatformConfigData Pointer to Platform Config Data Header @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR Unable to retrieve PCAT table @retval EFI_INVALID_PARAMETER pPlatformConfigData is NULL **/ STATIC EFI_STATUS GenerateOemPcdHeader ( IN OUT NVDIMM_CONFIGURATION_HEADER *pPlatformConfigData ) { EFI_STATUS ReturnCode = EFI_SUCCESS; if (pPlatformConfigData == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL) { NVDIMM_DBG("PCAT table not found"); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } pPlatformConfigData->Header.Signature = NVDIMM_CONFIGURATION_HEADER_SIG; pPlatformConfigData->Header.Length = sizeof (*pPlatformConfigData); // For Purley platforms, only one revision (0x1) for PCD Config Header is supported if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr)) { pPlatformConfigData->Header.Revision.AsUint8 = NVDIMM_CONFIGURATION_TABLES_REVISION_1; } else { pPlatformConfigData->Header.Revision.AsUint8 = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr->Header.Revision.AsUint8; } CopyMem_S(&pPlatformConfigData->Header.OemId, sizeof(pPlatformConfigData->Header.OemId), NVDIMM_CONFIGURATION_HEADER_OEM_ID, NVDIMM_CONFIGURATION_HEADER_OEM_ID_LEN); pPlatformConfigData->Header.OemTableId = NVDIMM_CONFIGURATION_HEADER_OEM_TABLE_ID; pPlatformConfigData->Header.OemRevision = NVDIMM_CONFIGURATION_HEADER_OEM_REVISION; pPlatformConfigData->Header.CreatorId = NVDIMM_CONFIGURATION_HEADER_CREATOR_ID; pPlatformConfigData->Header.CreatorRevision = NVDIMM_CONFIGURATION_HEADER_CREATOR_REVISION; GenerateChecksum(pPlatformConfigData, pPlatformConfigData->Header.Length, PCAT_TABLE_HEADER_CHECKSUM_OFFSET); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get Platform Config Data OEM partition Intel config region and check a correctness of header. We only return the actua PCD config data, from the first 64KiB of Intel FW/SW config metadata. The latter 64KiB is reserved for OEM use. The caller is responsible for a memory deallocation of the ppPlatformConfigData @param[in] pDimm The Intel NVM Dimm to retrieve PCD from @param[in] RestoreCorrupt If true will generate a default PCD when a corrupt header is found @param[out] ppPlatformConfigData Pointer to a new buffer pointer for storing retrieved data @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR Incorrect PCD header @retval Other return codes from GetPcdOemConfigDataUsingSmallPayload **/ EFI_STATUS GetPlatformConfigDataOemPartition ( IN DIMM *pDimm, IN BOOLEAN RestoreCorrupt, OUT NVDIMM_CONFIGURATION_HEADER **ppPlatformConfigData ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 PcdDataSize = 0; NVDIMM_ENTRY(); if (pDimm == NULL || ppPlatformConfigData == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /** Get current Platform Config Data oem partition from dimm **/ ReturnCode = GetPcdOemConfigDataUsingSmallPayload(pDimm, (UINT8 **)ppPlatformConfigData, &PcdDataSize); if(RestoreCorrupt && (EFI_NOT_FOUND == ReturnCode || EFI_VOLUME_CORRUPTED == ReturnCode)) { NVDIMM_WARN("Generating new OemPcdHeader due to missing or corrupt PCD config header."); *ppPlatformConfigData = AllocateZeroPool(sizeof(NVDIMM_CONFIGURATION_HEADER)); if (*ppPlatformConfigData == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = GenerateOemPcdHeader(*ppPlatformConfigData); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Generating new OemPcdHeader failed."); goto Finish; } goto Finish; } if (EFI_ERROR(ReturnCode) || *ppPlatformConfigData == NULL) { NVDIMM_DBG("Error calling Get Platform Config Data FW command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Set Platform Config Data OEM Partition Intel config region. We only write to the first 64KiB of Intel FW/SW config metadata. The latter 64KiB is reserved for OEM use. @param[in] pDimm The Intel NVM Dimm to set PCD @param[in] pNewConf Pointer to new config data to write @param[in] NewConfSize Size of pNewConf @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER NULL inputs or bad size @retval Other return codes from FwCmdSetPlatformConfigData **/ EFI_STATUS SetPlatformConfigDataOemPartition( IN DIMM *pDimm, IN NVDIMM_CONFIGURATION_HEADER *pNewConf, IN UINT32 NewConfSize ) { UINT8 *pOemPartition = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if ((pDimm == NULL) || (pNewConf == NULL)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if ((NewConfSize == 0) || (NewConfSize > PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE)) { NVDIMM_DBG("Bad NewConfSize"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /* Previous algorithm assumed read and write via large payload MB transactions, so required reading / writing entire PCD region. Switched to using SMALL MB, which allows writing only the relevant data, and preventing any writes > 64kb. This technique is faster given current size of PCD Data. */ // Write the PCD data via small payload MB. This call internally enforces using small payload MB // for any OEM Partition writes. ReturnCode = FwCmdSetPlatformConfigData(pDimm, PCD_OEM_PARTITION_ID, (UINT8*)pNewConf, NewConfSize); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to set Platform Config Data"); goto Finish; } Finish: FREE_POOL_SAFE(pOemPartition); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Matches FW return code to one of available EFI_STATUS EFI base types @param[in] Status - status byte returned from FW command @retval - Appropriate EFI_STATUS **/ EFI_STATUS MatchFwReturnCode ( IN UINT8 FwStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); switch (FwStatus) { case FW_SUCCESS: break; case FW_INVALID_COMMAND_PARAMETER: case FW_INVALID_ALIGNMENT: ReturnCode = EFI_INVALID_PARAMETER; break; case FW_DATA_TRANSFER_ERROR: case FW_INTERNAL_DEVICE_ERROR: case FW_NO_RESOURCES: ReturnCode = EFI_DEVICE_ERROR; break; case FW_UNSUPPORTED_COMMAND: case FW_INJECTION_NOT_ENABLED: ReturnCode = EFI_UNSUPPORTED; break; case FW_DEVICE_BUSY: ReturnCode = EFI_NO_RESPONSE; break; case FW_MEDIA_DISABLED: ReturnCode = EFI_NO_MEDIA; break; case FW_INCORRECT_PASSPHRASE: case FW_CONFIG_LOCKED: ReturnCode = EFI_ACCESS_DENIED; break; case FW_AUTH_FAILED: case FW_INVALID_SECURITY_STATE: ReturnCode = EFI_SECURITY_VIOLATION; break; case FW_DATA_NOT_SET: ReturnCode = EFI_NOT_STARTED; break; case FW_TIMEOUT_OCCURRED: ReturnCode = EFI_TIMEOUT; break; case FW_SYSTEM_TIME_NOT_SET: case FW_REVISION_FAILURE: case FW_INCOMPATIBLE_DIMM_TYPE: case FW_ABORTED: case FW_UPDATE_ALREADY_OCCURRED: ReturnCode = EFI_ABORTED; break; default: ReturnCode = EFI_ABORTED; break; } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #ifdef OS_BUILD /** Matches DSM return code to one of available EFI_STATUS EFI base types @param[in] DsmStatus - status byte returned from FW command @retval - Appropriate EFI_STATUS **/ EFI_STATUS MatchDsmReturnCode( IN UINT8 DsmStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); switch (DsmStatus) { case DSM_VENDOR_SUCCESS: break; default: ReturnCode = EFI_ABORTED; break; } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif /** Check if SKU conflict occurred. Any mixed modes between manageable DIMMs are prohibited on a platform. @param[in] pDimm1 - first DIMM to compare SKU mode @param[in] pDimm2 - second DIMM to compare SKU mode @retval NVM_SUCCESS - if everything went fine @retval NVM_ERR_DIMM_SKU_MODE_MISMATCH - if mode conflict occurred @retval NVM_ERR_DIMM_SKU_SECURITY_MISMATCH - if security mode conflict occurred **/ NvmStatusCode IsDimmSkuModeMismatch( IN DIMM *pDimm1, IN DIMM *pDimm2 ) { NvmStatusCode StatusCode = NVM_ERR_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pDimm1 == NULL || pDimm2 == NULL) { NVDIMM_DBG("Invalid parameter given to check SKU mismatch"); goto Finish; } if (!IsDimmManageable(pDimm1) || !IsDimmManageable(pDimm2)) { StatusCode = NVM_SUCCESS; goto Finish; } StatusCode = SkuComparison(*(UINT32 *)&pDimm1->SkuInformation, *(UINT32 *)&pDimm2->SkuInformation); Finish: NVDIMM_EXIT(); return StatusCode; } /** Calculate a size of capacity considered Reserved. It is the aligned PM capacity less the mapped AD capacity @param[in] Dimm to retrieve reserved size for @param[out] pReservedCapacity pointer to reserved capacity @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_ABORTED Failure to retrieve current memory mode @retval EFI_SUCCESS Success **/ EFI_STATUS GetReservedCapacity( IN DIMM *pDimm, OUT UINT64 *pReservedCapacity ) { EFI_STATUS ReturnCode = EFI_SUCCESS; MEMORY_MODE CurrentMode = MEMORY_MODE_1LM; if (pDimm == NULL || pReservedCapacity == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = CurrentMemoryMode(&CurrentMode); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to determine current memory mode"); goto Finish; } if (pDimm->Configured || IS_BIOS_VOLATILE_MEMORY_MODE_2LM(CurrentMode)) { *pReservedCapacity = ROUNDDOWN(pDimm->PmCapacity, REGION_PERSISTENT_SIZE_ALIGNMENT_B) - pDimm->MappedPersistentCapacity; } else { *pReservedCapacity = 0; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #define FW_TEMPERATURE_CONST_1 625 #define FW_TEMPERATURE_CONST_2 10000 /** Transform temperature in FW format to usual integer in Celsius @param[in] Temperature Temperature from FW @retval Value in Celsius **/ INT16 TransformFwTempToRealValue( IN TEMPERATURE Temperature ) { INT16 Value = 0; Value = (INT16) (((UINT64)Temperature.Separated.TemperatureValue * FW_TEMPERATURE_CONST_1) / FW_TEMPERATURE_CONST_2); if (Temperature.Separated.Sign == TEMPERATURE_NEGATIVE) { Value *= -1; } return Value; } /** Transform temperature from usual integer in Celsius to FW format @param[in] Value Temperature in Celsius @retval Temperature in FW format **/ TEMPERATURE TransformRealValueToFwTemp( IN INT16 Value ) { TEMPERATURE Temperature; ZeroMem(&Temperature, sizeof(Temperature)); if (Value >= 0) { Temperature.Separated.Sign = TEMPERATURE_POSITIVE; } else { Temperature.Separated.Sign = TEMPERATURE_NEGATIVE; /** Change to positive **/ Value *= -1; } Temperature.Separated.TemperatureValue = (UINT16) (((UINT64)Value * FW_TEMPERATURE_CONST_2) / FW_TEMPERATURE_CONST_1); return Temperature; } /** Get the Dimm UID (a globally unique NVDIMM identifier) for DIMM, as per the following representation defined in ACPI 6.1 specification: "%02x%02x-%02x-%02x%2x-%02x%02x%02x%02x" (if the Manufacturing Location and Manufacturing Date fields are valid) "%02x%02x-%02x%02x%02x%02x" (if the Manufacturing Location and Manufacturing Date fields are invalid) @param[in] pDimm DIMM for which the UID is being initialized @param[out] pDimmUid Array to store Dimm UID @param[in] DimmUidLen Size of pDimmUid @retval EFI_SUCCESS Dimm UID field was initialized successfully. @retval EFI_INVALID_PARAMETER one or more parameters are NULL. **/ EFI_STATUS GetDimmUid( IN DIMM *pDimm, OUT CHAR16 *pDimmUid, IN UINT32 DimmUidLen ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 *TmpDimmUid = NULL; if (pDimm == NULL || pDimmUid == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (pDimm->VendorId != 0 && pDimm->ManufacturingInfoValid != FALSE && pDimm->SerialNumber != 0) { TmpDimmUid = CatSPrint(NULL, L"%04x", EndianSwapUint16(pDimm->VendorId)); if (pDimm->ManufacturingInfoValid == TRUE) { TmpDimmUid = CatSPrintClean(TmpDimmUid, L"-%02x-%04x", pDimm->ManufacturingLocation, EndianSwapUint16(pDimm->ManufacturingDate)); } TmpDimmUid = CatSPrintClean(TmpDimmUid ,L"-%08x", EndianSwapUint32(pDimm->SerialNumber)); } else { TmpDimmUid = CatSPrint(NULL, L""); } if (TmpDimmUid != NULL) { StrnCpyS(pDimmUid, DimmUidLen, TmpDimmUid, DimmUidLen - 1); FREE_POOL_SAFE(TmpDimmUid); } Finish: NVDIMM_EXIT_CHECK_I64(ReturnCode); return ReturnCode; } /** Set Obj Status when DIMM is not found using Id expected by end user @param[in] DimmId the Pid for the DIMM that was not found @param[in] pDimms Pointer to head of list where DimmId should be found @param[out] pCommandStatus Pointer to command status structure **/ VOID SetObjStatusForDimmNotFound( IN UINT16 DimmId, IN LIST_ENTRY *pDimms, OUT COMMAND_STATUS *pCommandStatus ) { DIMM *pCurrentDimm = NULL; pCurrentDimm = GetDimmByPid(DimmId, pDimms); if (pCurrentDimm != NULL) { SetObjStatusForDimm(pCommandStatus, pCurrentDimm, NVM_ERR_DIMM_NOT_FOUND); pCurrentDimm = NULL; } else { SetObjStatus(pCommandStatus, DimmId, NULL, 0, NVM_ERR_DIMM_NOT_FOUND, ObjectTypeDimm); } } /** Set object status for DIMM @param[out] pCommandStatus Pointer to command status structure @param[in] pDimm DIMM for which the object status is being set @param[in] Status Object status to set **/ VOID SetObjStatusForDimm( OUT COMMAND_STATUS *pCommandStatus, IN DIMM *pDimm, IN NVM_STATUS Status ) { SetObjStatusForDimmWithErase(pCommandStatus, pDimm, Status, FALSE); } /** Set object status for DIMM @param[out] pCommandStatus Pointer to command status structure @param[in] pDimm DIMM for which the object status is being set @param[in] Status Object status to set @param[in] If TRUE - clear all other status before setting this one **/ VOID SetObjStatusForDimmWithErase( OUT COMMAND_STATUS *pCommandStatus, IN DIMM *pDimm, IN NVM_STATUS Status, IN BOOLEAN EraseFirst ) { CHAR16 DimmUid[MAX_DIMM_UID_LENGTH]; if (pDimm == NULL || pCommandStatus == NULL) { return; } ZeroMem(DimmUid, sizeof(DimmUid)); if (EFI_ERROR(GetDimmUid(pDimm, DimmUid, MAX_DIMM_UID_LENGTH))) { NVDIMM_ERR("Error in GetDimmUid"); return; } if (EraseFirst) { EraseObjStatus(pCommandStatus, pDimm->DeviceHandle.AsUint32, DimmUid, MAX_DIMM_UID_LENGTH, ObjectTypeDimm); } SetObjStatus(pCommandStatus, pDimm->DeviceHandle.AsUint32, DimmUid, MAX_DIMM_UID_LENGTH, Status, ObjectTypeDimm); } /** Get overwrite DIMM operation status for DIMM @param[in] pDimm DIMM to retrieve overwrite DIMM operation status from @param[out] pOverwriteDimmStatus Retrieved status @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ EFI_STATUS GetOverwriteDimmStatus( IN DIMM *pDimm, OUT UINT8 *pOverwriteDimmStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pDimm == NULL || pOverwriteDimmStatus == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pOverwriteDimmStatus = LONG_OP_STATUS_NOT_STARTED; ReturnCode = GetLongOpDimmInfoStatus(pDimm, PtSetSecInfo, SubopOverwriteDimm, pOverwriteDimmStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("GetLongOpDimmInfoStatus failed!"); goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get DIMM Info LongOp status based on FW LongOp status code @param[in] pDimm DIMM to retrieve overwrite DIMM operation status from @param[in] pOpcode Opcode of long op command @param[in] pOpcode Subopcode of long op command @param[out] pLongOpDimmInfoStatus Long Op status @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL or invalid **/ EFI_STATUS GetLongOpDimmInfoStatus( IN DIMM *pDimm, IN UINT8 Opcode, IN UINT8 SubOpcode, OUT UINT8 *pLongOpDimmInfoStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT8 FwStatus = FW_SUCCESS; PT_OUTPUT_PAYLOAD_FW_LONG_OP_STATUS LongOpStatus; NVDIMM_ENTRY(); ZeroMem(&LongOpStatus, sizeof(LongOpStatus)); if (pDimm == NULL || pLongOpDimmInfoStatus == NULL || !((Opcode == PtSetFeatures && SubOpcode == SubopAddressRangeScrub) || (Opcode == PtUpdateFw && SubOpcode == SubopUpdateFw) || (Opcode == PtSetSecInfo && SubOpcode == SubopOverwriteDimm))) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pLongOpDimmInfoStatus = LONG_OP_STATUS_NOT_STARTED; ReturnCode = FwCmdGetLongOperationStatus(pDimm, &FwStatus, &LongOpStatus); if (EFI_ERROR(ReturnCode)) { if ((pDimm->FwVer.FwApiMajor == 1 && pDimm->FwVer.FwApiMinor <= 4 && FwStatus == FW_INTERNAL_DEVICE_ERROR) || FwStatus == FW_DATA_NOT_SET) { /** It is valid case when there is no long operation status **/ *pLongOpDimmInfoStatus = LONG_OP_STATUS_NOT_STARTED; ReturnCode = EFI_SUCCESS; } else { *pLongOpDimmInfoStatus = LONG_OP_STATUS_UNKNOWN; } goto Finish; } if (LongOpStatus.CmdOpcode == Opcode && LongOpStatus.CmdSubOpcode == SubOpcode) { switch (LongOpStatus.Status) { case FW_SUCCESS: *pLongOpDimmInfoStatus = LONG_OP_STATUS_COMPLETED; break; case FW_DEVICE_BUSY: *pLongOpDimmInfoStatus = LONG_OP_STATUS_IN_PROGRESS; break; case FW_DATA_NOT_SET: *pLongOpDimmInfoStatus = LONG_OP_STATUS_NOT_STARTED; break; case FW_ABORTED: *pLongOpDimmInfoStatus = LONG_OP_STATUS_ABORTED; break; default: *pLongOpDimmInfoStatus = LONG_OP_STATUS_ERROR; break; } } else { *pLongOpDimmInfoStatus = LONG_OP_STATUS_UNKNOWN; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Poll while ARS long operation status reports DEVICE BUSY @param[in] pDimm DIMM to retrieve overwrite DIMM operation status from @param[in] TimeoutSecs - Poll timeout in seconds @retval EFI_SUCCESS Success @retval EFI_TIMEOUT Timeout expired and did not receive something other than FW_DEVICE_BUSY @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_DEVICE_ERROR Retrieved an unexpected opcode/subopcode when requesting long op status **/ EFI_STATUS PollOnArsDeviceBusy( IN DIMM *pDimm, IN UINT32 TimeoutSecs ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT8 FwStatus = FW_SUCCESS; PT_OUTPUT_PAYLOAD_FW_LONG_OP_STATUS LongOpStatus; UINT32 RetryMax = 0; UINT32 RetryCount = 0; NVDIMM_ENTRY(); ZeroMem(&LongOpStatus, sizeof(LongOpStatus)); if (pDimm == NULL) { goto Finish; } RetryMax = (TimeoutSecs * 1000000) / POLL_ARS_LONG_OP_DELAY_US; for(RetryCount = 0; RetryCount < RetryMax; ++RetryCount) { ReturnCode = FwCmdGetLongOperationStatus(pDimm, &FwStatus, &LongOpStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Error occurred while polling for ARS enable/disable state.\n"); break; } if (LongOpStatus.CmdOpcode == PtSetFeatures && LongOpStatus.CmdSubOpcode == SubopAddressRangeScrub) { if (FW_DEVICE_BUSY != LongOpStatus.Status) { ReturnCode = EFI_SUCCESS; goto Finish; } } else { NVDIMM_ERR("Unexpected opcode/subopcodes retrieved with Get Long Op Status\n"); ReturnCode = EFI_DEVICE_ERROR; break; } gBS->Stall(POLL_ARS_LONG_OP_DELAY_US); ZeroMem(&LongOpStatus, sizeof(LongOpStatus)); } if (EFI_SUCCESS == ReturnCode && RetryCount == RetryMax) { ReturnCode = EFI_TIMEOUT; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Customer Format Dimm Send a customer format command through the smbus @param[in] pDimm The dimm to attempt to format @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Invalid FW Command Parameter **/ EFI_STATUS FwCmdFormatDimm( IN DIMM *pDimm ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); if (pDimm == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (!pFwCmd) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->Opcode = PtCustomerFormat; ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode) && ReturnCode != EFI_TIMEOUT) { NVDIMM_DBG("Error detected when sending PtCustomerFormat command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get DDRT IO init info @param[in] pDimm Target DIMM structure pointer @param[out] pDdrtIoInitInfo pointer to filled payload with DDRT IO init info @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS FwCmdGetDdrtIoInitInfo( IN DIMM *pDimm, OUT PT_OUTPUT_PAYLOAD_GET_DDRT_IO_INIT_INFO *pDdrtIoInitInfo ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || pDdrtIoInitInfo == NULL) { goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetAdminFeatures; pFwCmd->SubOpcode = SubopDdrtIoInitInfo; pFwCmd->OutputPayloadSize = sizeof(*pDdrtIoInitInfo); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get DDRT IO init info"); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pDdrtIoInitInfo, sizeof(*pDdrtIoInitInfo), pFwCmd->OutPayload, sizeof(*pDdrtIoInitInfo)); Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get Command Access Policy for a specific command @param[IN] pDimm Target DIMM structure pointer @param[IN] Opcode for the command @param[IN] SubOpcode for the command @param[OUT] pRestricted code for applied restriction (0-3) @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS FwCmdGetCommandAccessPolicy( IN DIMM *pDimm, IN UINT8 Opcode, IN UINT8 Subopcode, OUT UINT8 *pRestriction ) { NVM_FW_CMD *pFwCmd = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; PT_INPUT_PAYLOAD_GET_COMMAND_ACCESS_POLICY *pInputCAP = NULL; PT_OUTPUT_PAYLOAD_GET_COMMAND_ACCESS_POLICY *pOutputCAP = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || pRestriction == NULL) { goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetAdminFeatures; pFwCmd->SubOpcode = SubopCommandAccessPolicy; pFwCmd->OutputPayloadSize = sizeof(PT_OUTPUT_PAYLOAD_GET_COMMAND_ACCESS_POLICY); pInputCAP = (PT_INPUT_PAYLOAD_GET_COMMAND_ACCESS_POLICY*) pFwCmd->InputPayload; pInputCAP->Opcode = Opcode; pInputCAP->Subopcode = Subopcode; pFwCmd->InputPayloadSize = sizeof(PT_INPUT_PAYLOAD_GET_COMMAND_ACCESS_POLICY); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending GetCommandAccessPolicy command (RC = 0x%x)", ReturnCode); NVDIMM_DBG("FW CMD Status 0x%x", pFwCmd->Status); if (pFwCmd->Status == FW_INVALID_COMMAND_PARAMETER) { ReturnCode = EFI_UNSUPPORTED; } else { FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); } goto Finish; } pOutputCAP = (PT_OUTPUT_PAYLOAD_GET_COMMAND_ACCESS_POLICY*) pFwCmd->OutPayload; *pRestriction = pOutputCAP->Restriction; Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Inject Temperature error payload @param[IN] pDimm Target DIMM structure pointer @param[IN] subopcode for error injection command @param[OUT] pInjectInputPayload - input payload to be sent @param[OUT] pFwStatus FW status returned by dimm @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS FwCmdInjectError( IN DIMM *pDimm, IN UINT8 SubOpCode, OUT VOID *pInjectInputPayload, OUT UINT8 *pFwStatus ) { NVM_FW_CMD *pFwCmd = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pDimm == NULL || pInjectInputPayload == NULL || pFwStatus == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtInjectError; pFwCmd->SubOpcode = SubOpCode; pFwCmd->InputPayloadSize = SMALL_PAYLOAD_SIZE; pFwCmd->OutputPayloadSize = 0; CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), pInjectInputPayload, pFwCmd->InputPayloadSize); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); *pFwStatus = pFwCmd->Status; if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to inject error, error: %x\n", ReturnCode); if (pFwCmd->Status == FW_INJECTION_NOT_ENABLED) { NVDIMM_DBG("FW Error injection is not enabled"); } FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get DIMMs system time @param[in] pDimm Target DIMM structure pointer @param[out] pSystemTimePayload pointer to filled payload @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS FwCmdGetSystemTime( IN DIMM *pDimm, OUT PT_SYSTEM_TIME_PAYLOAD *pSystemTimePayload ) { NVM_FW_CMD *pFwCmd = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pDimm == NULL || pSystemTimePayload == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetAdminFeatures; pFwCmd->SubOpcode = SubopSystemTime; pFwCmd->OutputPayloadSize = sizeof(*pSystemTimePayload); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Error detected when sending FwCmdGetSystemTime command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pSystemTimePayload, sizeof(*pSystemTimePayload), pFwCmd->OutPayload, sizeof(*pSystemTimePayload)); Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get extended ADR status info @param[in] pDimm Target DIMM structure pointer @param[out] pExtendedAdrInfo pointer to filled payload with extended ADR info @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null @retval EFI_UNSUPPORTED if FIS doesn't support Get Admin Features/Extended ADR **/ EFI_STATUS FwCmdGetExtendedAdrInfo( IN DIMM *pDimm, OUT PT_OUTPUT_PAYLOAD_GET_EADR *pExtendedAdrInfo ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || pExtendedAdrInfo == NULL) { goto Finish; } //Get Extended ADR status info is new to FIS2.0 if (pDimm->FwVer.FwApiMajor < 2) { ReturnCode = EFI_UNSUPPORTED; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetAdminFeatures; pFwCmd->SubOpcode = SubopExtendedAdr; pFwCmd->OutputPayloadSize = sizeof(*pExtendedAdrInfo); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get extended ADR info"); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pExtendedAdrInfo, sizeof(*pExtendedAdrInfo), pFwCmd->OutPayload, sizeof(*pExtendedAdrInfo)); Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Firmware command to get Latch System Shutdown State @param[in] pDimm Target DIMM structure pointer @param[out] pExtendedAdrInfo pointer to filled payload with Latch System Shutdown State info @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER if parameter provided is invalid @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetLatchSystemShutdownStateInfo( IN DIMM *pDimm, OUT PT_OUTPUT_PAYLOAD_GET_LATCH_SYSTEM_SHUTDOWN_STATE *pLastSystemShutdownStateInfo ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || pLastSystemShutdownStateInfo == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pFwCmd = AllocateZeroPool(sizeof(*pFwCmd)); if (pFwCmd == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtGetAdminFeatures; pFwCmd->SubOpcode = SubopLatchSystemShutdownState; pFwCmd->OutputPayloadSize = sizeof(*pLastSystemShutdownStateInfo); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to get Latch System Shutdown State info"); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pLastSystemShutdownStateInfo, sizeof(*pLastSystemShutdownStateInfo), pFwCmd->OutPayload, sizeof(*pLastSystemShutdownStateInfo)); Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Helper function to run a simple small payload command. @param[in] pDimm Target DIMM structure pointer @param[in] Opcode Firmware command opcode to use @param[in] SubOpcode Firmware command sub-opcode to use @param[in] InputPayload Input payload buffer @param[in] InputPayloadSize Input payload buffer size @param[in] OutputPayload Output payload buffer @param[in] OutputPayloadSize Output payload buffer size @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER if parameter provided is invalid @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdSmallPayload( IN DIMM *pDimm, IN UINT8 Opcode, IN UINT8 SubOpcode, IN UINT8 *InputPayload OPTIONAL, IN UINT8 InputPayloadSize, OUT UINT8 *OutputPayload OPTIONAL, IN UINT8 OutputPayloadSize ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVM_FW_CMD *pFwCmd = NULL; NVDIMM_ENTRY(); CHECK_NULL_ARG(pDimm, Finish); CHECK_RESULT_MALLOC(pFwCmd, AllocateZeroPool(sizeof(*pFwCmd)), Finish); pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = Opcode; pFwCmd->SubOpcode = SubOpcode; pFwCmd->InputPayloadSize = InputPayloadSize; if (InputPayloadSize > 0 && InputPayload != NULL) { CopyMem_S(pFwCmd->InputPayload, sizeof(pFwCmd->InputPayload), InputPayload, InputPayloadSize); } pFwCmd->OutputPayloadSize = OutputPayloadSize; ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { // This macros is also responsible for printing out useful error messages FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } if (OutputPayloadSize > 0 && OutputPayload != NULL) { CopyMem_S(OutputPayload, OutputPayloadSize, pFwCmd->OutPayload, sizeof(pFwCmd->OutPayload)); } Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check if DIMM is manageable @param[in] pDimm the DIMM struct @retval BOOLEAN whether or not dimm is manageable **/ BOOLEAN IsDimmManageable( IN DIMM *pDimm ) { BOOLEAN DimmManageable = FALSE; if (pDimm == NULL) { return FALSE; } DimmManageable = IsDimmManageableByValues(pDimm->SubsystemVendorId, pDimm->FmtInterfaceCodeNum, pDimm->FmtInterfaceCode, pDimm->SubsystemDeviceId, pDimm->FwVer.FwApiMajor, pDimm->FwVer.FwApiMinor); if (!DimmManageable) { NVDIMM_ERR("Dimm 0x%x unmanageable! 0x%x, Num:0x%x, #0:0x%x, 0x%x, FIS %x.%x", pDimm->DeviceHandle, pDimm->SubsystemVendorId, pDimm->FmtInterfaceCodeNum, pDimm->FmtInterfaceCode[0], pDimm->SubsystemDeviceId, pDimm->FwVer.FwApiMajor, pDimm->FwVer.FwApiMinor); } return DimmManageable; } /** Check if DIMM is in supported config @param[in] pDimm the DIMM struct @retval BOOLEAN whether or not dimm is in supported config **/ BOOLEAN IsDimmInSupportedConfig( IN DIMM *pDimm ) { if (pDimm == NULL) { return FALSE; } return !IsDimmInUnmappedPopulationViolation(pDimm); } /** Check if DIMM is in population violation @param[in] pDimm the DIMM struct @retval BOOLEAN whether or not dimm is in population violation **/ BOOLEAN IsDimmInPopulationViolation( IN DIMM *pDimm ) { if (pDimm == NULL) { return FALSE; } return (IsDimmInUnmappedPopulationViolation(pDimm) || IsDimmInPmMappedPopulationViolation(pDimm)); } /** Check if DIMM is in population violation and fully unmapped @param[in] pDimm the DIMM struct @retval BOOLEAN whether or not dimm is in population violation and fully unmapped **/ BOOLEAN IsDimmInUnmappedPopulationViolation( IN DIMM *pDimm ) { if (pDimm == NULL) { return FALSE; } return (DIMM_CONFIG_DCPMM_POPULATION_ISSUE == pDimm->ConfigStatus); } /** Check if DIMM is in population violation and persistent memory is still mapped @param[in] pDimm the DIMM struct @retval BOOLEAN whether or not dimm is in population violation and persistent memory is still mapped **/ BOOLEAN IsDimmInPmMappedPopulationViolation( IN DIMM *pDimm ) { if (pDimm == NULL) { return FALSE; } return (DIMM_CONFIG_PM_MAPPED_VM_POPULATION_ISSUE == pDimm->ConfigStatus); } /** Check if PMem module is accessible through small payload based on the specified transport protocol @param[in] pDimm the DIMM struct @param[in] Protocol transport protocol to be used @param[out] pIsInterfaceReady whether or not specified dimm interface is ready @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL or invalid **/ EFI_STATUS GetDimmInterfaceStatus( IN DIMM *pDimm, IN TRANSPORT_PROTOCOL Protocol, OUT BOOLEAN *pIsInterfaceReady ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TempReturnCode = EFI_SUCCESS; PT_ID_DIMM_PAYLOAD *pPayload = NULL; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS AttribsOrig; EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS AttribsTemp; NVDIMM_ENTRY(); if (pDimm == NULL || pIsInterfaceReady == NULL || (Protocol != FisTransportDdrt && Protocol != FisTransportSmbus)) { NVDIMM_DBG("Invalid parameter."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ZeroMem(&AttribsOrig, sizeof(AttribsOrig)); ZeroMem(&AttribsTemp, sizeof(AttribsTemp)); *pIsInterfaceReady = FALSE; /** The main reliable way to determine if ddrt/smbus is accessible or not is to try a command over that interface. The only way currently to force the interface is to use the global "-ddrt"/"-smbus" flags. Since the user setting these flags on the CLI conflict with this operation, save off the previous state of these flags so we can force which interface to use and then restore the original interface flags afterwards. **/ CHECK_RESULT(OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL), Finish); CHECK_RESULT(pNvmDimmConfigProtocol->GetFisTransportAttributes(pNvmDimmConfigProtocol, &AttribsOrig), Finish); // Set the specified transport protocol and "-spmb" flags AttribsTemp.Protocol = Protocol; AttribsTemp.PayloadSize = FisTransportSizeSmallMb; CHECK_RESULT(pNvmDimmConfigProtocol->SetFisTransportAttributes(pNvmDimmConfigProtocol, AttribsTemp), Finish); CHECK_RESULT_MALLOC(pPayload, AllocateZeroPool(sizeof(*pPayload)), Finish); // Send identify dimm command to determine the interface status TempReturnCode = FwCmdSmallPayload(pDimm, PtIdentifyDimm, SubopIdentify, NULL, 0, (UINT8 *)pPayload, sizeof(*pPayload)); if (!EFI_ERROR(TempReturnCode)) { *pIsInterfaceReady = TRUE; } // Restore AttribsOrig CHECK_RESULT(pNvmDimmConfigProtocol->SetFisTransportAttributes(pNvmDimmConfigProtocol, AttribsOrig), Finish); Finish: FREE_POOL_SAFE(pPayload); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check if the dimm interface code of this DIMM is supported @param[in] pDimm the DIMM struct @retval true if supported, false otherwise **/ BOOLEAN IsDimmInterfaceCodeSupported( IN DIMM *pDimm ) { if (pDimm == NULL) { return FALSE; } return IsDimmInterfaceCodeSupportedByValues(pDimm->FmtInterfaceCodeNum, pDimm->FmtInterfaceCode); } /** Check if the subsystem device ID of this DIMM is supported @param[in] pDimm the DIMM struct @retval true if supported, false otherwise **/ BOOLEAN IsSubsystemDeviceIdSupported( IN DIMM *pDimm ) { if (pDimm == NULL) { return FALSE; } return IsSubsystemDeviceIdSupportedByValues( pDimm->SubsystemDeviceId); } /** Check if current firmware API version is supported @param[in] pDimm the DIMM struct @retval true if supported, false otherwise **/ BOOLEAN IsFwApiVersionSupported( IN DIMM *pDimm ) { if (pDimm == NULL) { return FALSE; } return IsFwApiVersionSupportedByValues(pDimm->FwVer.FwApiMajor, pDimm->FwVer.FwApiMinor); } /** Clears the PCD Cache on each DIMM in the global DIMM list @retval EFI_SUCCESS Success **/ EFI_STATUS ClearPcdCacheOnDimmList(VOID) { #ifdef PCD_CACHE_ENABLED DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; if (NULL != gNvmDimmData) { LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { if (pDimmNode == NULL) { // Not supposed to get a NULL pDimmNode, but just in case // exit out so we can't dereference it break; } pDimm = DIMM_FROM_NODE(pDimmNode); if (NULL != pDimm) { // Free memory and set to NULL so won't be used by Get PCD calls FREE_POOL_SAFE(pDimm->pPcdOem); } } } #endif // PCD_CACHE_ENABLED return EFI_SUCCESS; } /** Return what passthru method will be used to send the command. @param[in] pDimm The DCPMM to transact with @param[in] Opcode of the FW command. Used to determine if command is dependent on DDRT/SMBUS interface status. @param[in] SubOpcode of the FW command. Used to determine if command is dependent on DDRT/SMBUS interface status. @param[in] IsLargePayloadCommand Need to know if large payload interface is even desired. If not, then it makes no sense to write to the large payload mailbox unless the user specifies it. @param[out] Method Pointer to passthru method variable to modify @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if we failed to do basic communication with the DCPMM **/ EFI_STATUS DeterminePassThruMethod( IN DIMM *pDimm, IN UINT8 Opcode, IN UINT8 SubOpcode, IN BOOLEAN IsLargePayloadCommand, OUT DIMM_PASSTHRU_METHOD *pMethod ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS Attribs; if (pDimm == NULL || pMethod == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Initialize incoming variable to a good default, just in case *pMethod = DimmPassthruSmbusSmallPayload; CHECK_RESULT(OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL), Finish); CHECK_RESULT(pNvmDimmConfigProtocol->GetFisTransportAttributes(pNvmDimmConfigProtocol, &Attribs), Finish); // Check if the user manually specified a certain interface. If specified, // go to passthru directly and don't do any auto-detection. // Ignore "default" settings here, they'll be used later if ((Attribs.Protocol != FisTransportAuto || Attribs.PayloadSize != FisTransportSizeAuto) && // Skip this flow if only large payload was disabled (the default setting in OS) !(FisTransportAuto == Attribs.Protocol && FisTransportSizeSmallMb == Attribs.PayloadSize)) { // User only specified "-ddrt" if (IS_DDRT_FLAG_ENABLED(Attribs) && Attribs.PayloadSize == FisTransportSizeAuto) { if (IsLargePayloadCommand) { *pMethod = DimmPassthruDdrtLargePayload; } else { *pMethod = DimmPassthruDdrtSmallPayload; } } else if (IS_DDRT_FLAG_ENABLED(Attribs) && IS_LARGE_PAYLOAD_FLAG_ENABLED(Attribs)) { *pMethod = DimmPassthruDdrtLargePayload; } else if (IS_DDRT_FLAG_ENABLED(Attribs) && IS_SMALL_PAYLOAD_FLAG_ENABLED(Attribs)) { *pMethod = DimmPassthruDdrtSmallPayload; } else if (IS_SMBUS_FLAG_ENABLED(Attribs)) { *pMethod = DimmPassthruSmbusSmallPayload; } else { NVDIMM_ERR("Invalid Attribs state of %d, %d detected. Exiting", Attribs.Protocol, Attribs.PayloadSize); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } goto Finish; } // Check if one of DDRT or SMBUS interfaces is ready if (DIMM_DDRT_AND_SMBUS_INTERFACES_DOWN(pDimm->BootStatusBitmask) && !FW_CMD_INTERFACE_INDEPENDENT(Opcode, SubOpcode)) { ReturnCode = EFI_DEVICE_ERROR; NVDIMM_ERR("PMem module ddrt and smbus interfaces not available. Cancelling PassThru()"); goto Finish; } // If caller wants to send a large payload command if (TRUE == IsLargePayloadCommand && // and if no problems found with sending large payload !(FisTransportSizeSmallMb == Attribs.PayloadSize || (DIMM_MEDIA_NOT_ACCESSIBLE(pDimm->BootStatusBitmask)) || (pDimm->BootStatusBitmask & DIMM_BOOT_STATUS_DDRT_NOT_READY))) { // Then allow them to do so *pMethod = DimmPassthruDdrtLargePayload; // Otherwise prefer small payload DDRT } else if (!(pDimm->BootStatusBitmask & DIMM_BOOT_STATUS_DDRT_NOT_READY)) { *pMethod = DimmPassthruDdrtSmallPayload; } else { // Otherwise last resort is small payload smbus *pMethod = DimmPassthruSmbusSmallPayload; } Finish: return ReturnCode; } /** Check if sending a large payload command over the DDRT large payload mailbox is possible. Used by callers often to determine chunking behavior. @param[in] pDimm The DCPMM to transact with @param[out] pAvailable Whether large payload is available. Pointer to boolean variable @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if we failed to do basic communication with the DCPMM **/ EFI_STATUS IsLargePayloadAvailable( IN DIMM *pDimm, OUT BOOLEAN *pAvailable ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM_PASSTHRU_METHOD Method = DimmPassthruDdrtLargePayload; if (pDimm == NULL || pAvailable == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // TRUE -> We are attempting to send a large payload command // Only return true if DDRT large payload is allowed CHECK_RESULT(DeterminePassThruMethod(pDimm, 0, 0, TRUE, &Method), Finish); *pAvailable = DimmPassthruDdrtLargePayload == Method; Finish: return ReturnCode; } EFI_STATUS PassThru( IN struct _DIMM *pDimm, IN OUT NVM_FW_CMD *pCmd, IN UINT64 Timeout ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM_PASSTHRU_METHOD Method = DimmPassthruDdrtLargePayload; BOOLEAN IsLargePayloadCommand = FALSE; #ifdef OS_BUILD UINT8 InputPayloadTemp[IN_PAYLOAD_SIZE]; NVM_INPUT_PAYLOAD_SMBUS_OS_PASSTHRU *pInputPayloadSOP = NULL; #endif if (pDimm == NULL || pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } IsLargePayloadCommand = pCmd->LargeInputPayloadSize > 0 || pCmd->LargeOutputPayloadSize > 0; CHECK_RESULT(DeterminePassThruMethod(pDimm, pCmd->Opcode, pCmd->SubOpcode, IsLargePayloadCommand, &Method), Finish); // Obviously not ideal implementation, but ran into issues getting %s working // on Linux with NVDIMM_* prints. Need something working now though. if (DimmPassthruDdrtLargePayload == Method) { NVDIMM_DBG("Calling 0x%x:0x%x over ddrt lp on DCPMM 0x%x", pCmd->Opcode, pCmd->SubOpcode, pDimm->DeviceHandle.AsUint32); } else if (DimmPassthruDdrtSmallPayload == Method) { NVDIMM_DBG("Calling 0x%x:0x%x over ddrt sp on DCPMM 0x%x", pCmd->Opcode, pCmd->SubOpcode, pDimm->DeviceHandle.AsUint32); } else if (DimmPassthruSmbusSmallPayload == Method) { NVDIMM_DBG("Calling 0x%x:0x%x over smbus on DCPMM 0x%x", pCmd->Opcode, pCmd->SubOpcode, pDimm->DeviceHandle.AsUint32); } if (DimmPassthruSmbusSmallPayload == Method) { #ifdef OS_BUILD // SMBUS: Use a special bios emulated command, which BIOS will interpret // as a passthru to the DCPMM through the interface of choice // Carefully copy the buffers CopyMem(InputPayloadTemp, pCmd->InputPayload, IN_PAYLOAD_SIZE); ZeroMem(pCmd->InputPayload, IN_PAYLOAD_SIZE + IN_PAYLOAD_SIZE_EXT_PAD); // Re-interpret the existing input payload, now using the full buffer pInputPayloadSOP = (NVM_INPUT_PAYLOAD_SMBUS_OS_PASSTHRU *)(pCmd->InputPayload); CopyMem(pInputPayloadSOP->Data, InputPayloadTemp, IN_PAYLOAD_SIZE); // Update the rest of the parameters pCmd->InputPayloadSize = IN_PAYLOAD_SIZE + IN_PAYLOAD_SIZE_EXT_PAD; pInputPayloadSOP->Opcode = pCmd->Opcode; pInputPayloadSOP->SubOpcode = pCmd->SubOpcode; pInputPayloadSOP->Timeout = PT_TIMEOUT_INTERVAL_EXT; // Specify SMBUS pInputPayloadSOP->TransportInterface = SmbusTransportInterface; pCmd->Opcode = PtEmulatedBiosCommands; pCmd->SubOpcode = SubopExtVendorSpecific; } // Use the OS passthru dsm mechanism to talk with the DCPMM // for both DDRT and SMBUS ReturnCode = DefaultPassThru(pDimm, pCmd, PT_TIMEOUT_INTERVAL); // If we're using the special bios emulated command (smbus only // for now), do some cleanup and restore previous pCmd values if (DimmPassthruSmbusSmallPayload == Method) { pCmd->Opcode = pInputPayloadSOP->Opcode; pCmd->SubOpcode = pInputPayloadSOP->SubOpcode; ZeroMem(pInputPayloadSOP, IN_PAYLOAD_SIZE + IN_PAYLOAD_SIZE_EXT_PAD); CopyMem(pCmd->InputPayload, InputPayloadTemp, IN_PAYLOAD_SIZE); pCmd->InputPayloadSize = IN_PAYLOAD_SIZE; } if (EFI_ERROR(ReturnCode)) { goto Finish; } #else // SMBUS: Use the bios DCPMM protocol to send commands to the DCPMM CHECK_RESULT(DcpmmCmd(pDimm, pCmd, DCPMM_TIMEOUT_INTERVAL, FisOverSmbus), Finish); } else { // DDRT: Use the bios DCPMM protocol to send commands to the DCPMM CHECK_RESULT(DcpmmCmd(pDimm, pCmd, DCPMM_TIMEOUT_INTERVAL, FisOverDdrt), Finish); } #endif // OS_BUILD Finish: return ReturnCode; } /** Makes Bios emulated pass through call and acquires the DCPMM Boot Status Register @param[in] pDimm The DCPMM to retrieve BSR from @param[out] pBsrValue Pointer to memory to copy BSR value to @retval EFI_SUCCESS: Success @retval EFI_OUT_OF_RESOURCES: memory allocation failure **/ EFI_STATUS EFIAPI FwCmdGetBsr( IN DIMM *pDimm, OUT UINT64 *pBsrValue ) { NVM_FW_CMD *pFwCmd = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pDimm == NULL || pBsrValue == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } CHECK_RESULT_MALLOC(pFwCmd, AllocateZeroPool(sizeof(*pFwCmd)), Finish); pFwCmd->DimmID = pDimm->DimmID; pFwCmd->Opcode = PtEmulatedBiosCommands; pFwCmd->SubOpcode = SubopGetBSR; pFwCmd->OutputPayloadSize = sizeof(unsigned long long); ReturnCode = PassThru(pDimm, pFwCmd, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error detected when sending BIOS emulated GetBSR command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode); goto Finish; } CopyMem_S(pBsrValue, sizeof(*pBsrValue), pFwCmd->OutPayload, sizeof(UINT64)); NVDIMM_DBG("Bsr received is 0x%x", *pBsrValue); if ((*pBsrValue == MAX_UINT64_VALUE) || (*pBsrValue == 0)) { // Invalid values returned in BSR. Return failure CHECK_RESULT(EFI_NO_RESPONSE, Finish); } Finish: FREE_POOL_SAFE(pFwCmd); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Populate interface bits in boot status bitmask based on PMem module DDRT(and optionally SMBUS) interface status @param[in] pDimm Pointer to PMem module @param[out] pBootStatusBitmask Pointer to boot status bitmask @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ EFI_STATUS PopulateDimmBootStatusBitmaskInterfaceBits( IN DIMM *pDimm, OUT UINT16 *pBootStatusBitmask ) { EFI_STATUS ReturnCode = EFI_SUCCESS; BOOLEAN IsDdrtReady = FALSE; BOOLEAN IsSmbusReady = FALSE; NVDIMM_ENTRY(); if (pDimm == NULL || pBootStatusBitmask == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Clear interface bits from boot status bitmask RESET_DIMM_BOOT_STATUS_INTERFACE_BITS(*pBootStatusBitmask); // Check DDRT/SMBUS interface status // Check ddrt small payload access CHECK_RESULT(GetDimmInterfaceStatus(pDimm, FisTransportDdrt, &IsDdrtReady), Finish); if (!IsDdrtReady) { /** We try checking the smbus interface only if DDRT fails. Big performance penalty in OS currently if we use smbus. **/ *pBootStatusBitmask = DIMM_BOOT_STATUS_DDRT_NOT_READY; // Check smbus small payload access CHECK_RESULT(GetDimmInterfaceStatus(pDimm, FisTransportSmbus, &IsSmbusReady), Finish); if (!IsSmbusReady) { *pBootStatusBitmask |= DIMM_BOOT_STATUS_SMBUS_NOT_READY; goto Finish; } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Populate BSR bits in PMem module boot status bitmask based on boot status register value @param[in] pDimm Pointer to PMem module @param[in] pBsr Pointer to Boot Status Register(BSR) @param[out] pBootStatusBitmask Pointer to boot status bitmask @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ EFI_STATUS PopulateDimmBootStatusBitmaskBsrBits( IN DIMM *pDimm, IN DIMM_BSR *pBsr, OUT UINT16 *pBootStatusBitmask ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pDimm == NULL || pBsr == NULL || pBootStatusBitmask == NULL || (pBsr->AsUint64 == MAX_UINT64_VALUE) || (pBsr->AsUint64 == 0)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Clear BSR bits from boot status bitmask RESET_DIMM_BOOT_STATUS_BSR_BITS(*pBootStatusBitmask); if (pBsr->Separated_Current_FIS.MR == DIMM_BSR_MEDIA_NOT_TRAINED) { *pBootStatusBitmask |= DIMM_BOOT_STATUS_MEDIA_NOT_READY; } if (pBsr->Separated_Current_FIS.MR == DIMM_BSR_MEDIA_ERROR) { *pBootStatusBitmask |= DIMM_BOOT_STATUS_MEDIA_ERROR; } if (pBsr->Separated_Current_FIS.MD == DIMM_BSR_MEDIA_DISABLED) { *pBootStatusBitmask |= DIMM_BOOT_STATUS_MEDIA_DISABLED; } if (pBsr->Separated_Current_FIS.MBR == DIMM_BSR_MAILBOX_NOT_READY) { *pBootStatusBitmask |= DIMM_BOOT_STATUS_MAILBOX_NOT_READY; } if (pBsr->Separated_Current_FIS.RR == DIMM_BSR_REBOOT_REQUIRED) { *pBootStatusBitmask |= DIMM_BOOT_STATUS_REBOOT_REQUIRED; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Helper function for setting appropriate command status and return code when checking for mixed SKU condition @param[out] pCommandStatus Pointer to command status variable @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pCommandStatus is NULL @retval EFI_UNSUPPORTED Mixed SKU condition occurred **/ EFI_STATUS BlockMixedSku( OUT COMMAND_STATUS* pCommandStatus ) { DIMM* pDimm = NULL; LIST_ENTRY* pNode = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; if (pCommandStatus == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gNvmDimmData->PMEMDev.DimmSkuConsistency == FALSE) { ReturnCode = EFI_UNSUPPORTED; ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_NOT_SUPPORTED_BY_MIXED_SKU); LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pNode); if (pDimm->MixedSKUOffender) { SetObjStatusForDimm(pCommandStatus, pDimm, NVM_ERR_INCOMPATIBLE_SKU_ON_MODULE); } } } Finish: return ReturnCode; } #ifndef OS_BUILD /** Passthrough FIS command by Dcpmm BIOS protocol. @param[in] pDimm Target DIMM structure pointer @param[in, out] pCmd Firmware command structure pointer @param[in] Timeout Optional command timeout in microseconds @param[in] DcpmmInterface Interface for FIS request @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS DcpmmCmd( IN struct _DIMM *pDimm, IN OUT NVM_FW_CMD *pCmd, IN UINT32 Timeout OPTIONAL, IN DCPMM_FIS_INTERFACE DcpmmInterface ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DCPMM_FIS_INPUT *pInputPayload = NULL; DCPMM_FIS_OUTPUT *pOutputPayload = NULL; DCPMM_FIS_OUTPUT *pLargePayloadInfo = NULL; UINT16 Command = 0; NVDIMM_ENTRY(); if(pDimm == NULL || pCmd == NULL) { goto Finish; } pInputPayload = (DCPMM_FIS_INPUT *)AllocateZeroPool(sizeof(*pInputPayload) + pCmd->InputPayloadSize); if (pInputPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } if (pCmd->OutputPayloadSize > 0) { pOutputPayload = (DCPMM_FIS_OUTPUT *)AllocateZeroPool(sizeof(*pOutputPayload) + pCmd->OutputPayloadSize); if (pOutputPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } } /** Get large payload info **/ if (pCmd->LargeInputPayloadSize > 0 || pCmd->LargeOutputPayloadSize > 0) { pLargePayloadInfo = (DCPMM_FIS_OUTPUT *)AllocateZeroPool(sizeof(*pLargePayloadInfo)); if (pLargePayloadInfo == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = DcpmmLargePayloadInfo(pDimm, Timeout, DcpmmInterface, pLargePayloadInfo, &pCmd->Status); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Error detected when sending DcpmmLargePayloadInfo"); FW_CMD_ERROR_TO_EFI_STATUS(pCmd, ReturnCode); goto Finish; } } /** Prepare input payload structure **/ Command = (UINT16)pCmd->SubOpcode << EXT_SUB_OP_SHIFT | (UINT16)pCmd->Opcode; pInputPayload->Head.FisCmd = Command; pInputPayload->Head.DataSize = pCmd->InputPayloadSize; CopyMem_S(pInputPayload->Data.Fis.Payload, pCmd->InputPayloadSize, pCmd->InputPayload, pCmd->InputPayloadSize); /** Prepare output payload structure **/ if (pCmd->OutputPayloadSize > 0) { pOutputPayload->Head.DataSize = pCmd->OutputPayloadSize; } /** Write data to large input payload **/ if (pCmd->LargeInputPayloadSize > 0) { if (pCmd->LargeInputPayloadSize > pLargePayloadInfo->Data.LpInfo.InpPayloadSize) { NVDIMM_ERR("Available large input payload size is not enough"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } else { ReturnCode = DcpmmLargePayloadWrite(pDimm, pCmd->LargeInputPayload, pCmd->LargeInputPayloadSize, pLargePayloadInfo->Data.LpInfo.DataChunkSize, Timeout, DcpmmInterface, &pCmd->Status); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Error detected when sending DcpmmLargePayloadWrite"); FW_CMD_ERROR_TO_EFI_STATUS(pCmd, ReturnCode); goto Finish; } } } ReturnCode = gNvmDimmData->pDcpmmProtocol->DcpmmFisRequest( DcpmmInterface, pDimm->DeviceHandle.AsUint32, pInputPayload, pOutputPayload, Timeout, &pCmd->Status ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error detected when sending DcpmmFisRequest command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); FW_CMD_ERROR_TO_EFI_STATUS(pCmd, ReturnCode); goto Finish; } if (pCmd->OutputPayloadSize > 0) { CopyMem_S(pCmd->OutPayload, pCmd->OutputPayloadSize, pOutputPayload->Data.Fis.Payload, pCmd->OutputPayloadSize); } /** Read data from large output payload **/ if (pCmd->LargeOutputPayloadSize > 0) { if (pCmd->LargeOutputPayloadSize > pLargePayloadInfo->Data.LpInfo.OutPayloadSize) { NVDIMM_ERR("Data in large output payload cannot be fully filled"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } else { ReturnCode = DcpmmLargePayloadRead(pDimm, pCmd->LargeOutputPayloadSize, pLargePayloadInfo->Data.LpInfo.DataChunkSize, Timeout, DcpmmInterface, pCmd->LargeOutputPayload, &pCmd->Status); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Error detected when sending DcpmmLargePayloadRead"); FW_CMD_ERROR_TO_EFI_STATUS(pCmd, ReturnCode); goto Finish; } } } Finish: FREE_POOL_SAFE(pInputPayload); FREE_POOL_SAFE(pOutputPayload); FREE_POOL_SAFE(pLargePayloadInfo); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get large payload info by Dcpmm BIOS protocol. @param[in] pDimm Target DIMM structure pointer @param[in] Timeout Optional command timeout in microseconds @param[in] DcpmmInterface Interface for FIS request @param[out] pOutput Large payload info output data buffer @param[out] pStatus FIS request status @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS DcpmmLargePayloadInfo( IN struct _DIMM *pDimm, IN UINT32 Timeout OPTIONAL, IN DCPMM_FIS_INTERFACE DcpmmInterface, OUT DCPMM_FIS_OUTPUT *pOutput, OUT UINT8 *pStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DCPMM_FIS_INPUT *pLargeInputPayload = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || pOutput == NULL || pStatus == NULL) { goto Finish; } pLargeInputPayload = (DCPMM_FIS_INPUT *)AllocateZeroPool(sizeof(*pLargeInputPayload)); if (pLargeInputPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pLargeInputPayload->Head.FisCmd = (UINT16)FIS_CMD_GET_LP_MB_INFO; pLargeInputPayload->Head.DataSize = 0; pOutput->Head.DataSize = sizeof(pOutput->Data.LpInfo); ReturnCode = gNvmDimmData->pDcpmmProtocol->DcpmmFisRequest( DcpmmInterface, pDimm->DeviceHandle.AsUint32, pLargeInputPayload, pOutput, Timeout, pStatus ); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Error detected when sending DcpmmFisRequest command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } Finish: FREE_POOL_SAFE(pLargeInputPayload); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Write large payload by Dcpmm BIOS protocol. @param[in] pDimm Target DIMM structure pointer @param[in] pInput Input data buffer @param[in] InputSize Total input data size @param[in] MaxChunkSize Maximum chunk of data to write @param[in] Timeout Optional command timeout in microseconds @param[in] DcpmmInterface Interface for FIS request @param[out] pStatus FIS request status @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS DcpmmLargePayloadWrite( IN struct _DIMM *pDimm, IN UINT8 *pInput, IN UINT32 InputSize, IN UINT32 MaxChunkSize, IN UINT32 Timeout OPTIONAL, IN DCPMM_FIS_INTERFACE DcpmmInterface, OUT UINT8 *pStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DCPMM_FIS_INPUT *pLargeInputPayload = NULL; UINT32 Offset = 0; UINT32 CurrentChunkSize = MaxChunkSize < InputSize ? MaxChunkSize : InputSize; NVDIMM_ENTRY(); if (InputSize <= 0 || pDimm == NULL || pInput == NULL || pStatus == NULL) { goto Finish; } pLargeInputPayload = (DCPMM_FIS_INPUT *)AllocateZeroPool(sizeof(*pLargeInputPayload) + CurrentChunkSize); if (pLargeInputPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pLargeInputPayload->Head.FisCmd = (UINT16)FIS_CMD_WRITE_LP_INPUT_MB; pLargeInputPayload->Head.DataSize = sizeof(pLargeInputPayload->Data.LpWrite) + MaxChunkSize; for (Offset = 0; Offset < InputSize; Offset += MaxChunkSize) { pLargeInputPayload->Data.LpWrite.Offset = Offset; if (InputSize - Offset < MaxChunkSize) { CurrentChunkSize = InputSize - Offset; pLargeInputPayload->Head.DataSize = sizeof(pLargeInputPayload->Data.LpWrite) + CurrentChunkSize; pLargeInputPayload->Data.LpWrite.Size = CurrentChunkSize; } CopyMem_S(pLargeInputPayload->Data.LpWrite.Payload, CurrentChunkSize, pInput + Offset, CurrentChunkSize); ReturnCode = gNvmDimmData->pDcpmmProtocol->DcpmmFisRequest( DcpmmInterface, pDimm->DeviceHandle.AsUint32, pLargeInputPayload, NULL, Timeout, pStatus ); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Error detected when sending DcpmmFisRequest command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } } Finish: FREE_POOL_SAFE(pLargeInputPayload); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Read large payload by Dcpmm BIOS protocol. @param[in] pDimm Target DIMM structure pointer @param[in] OutputSize Total output data size @param[in] MaxChunkSize Maximum chunk of data to read @param[in] Timeout Optional command timeout in microseconds @param[in] DcpmmInterface Interface for FIS request @param[out] pOutput Output data buffer @param[out] pStatus FIS request status pointer @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS DcpmmLargePayloadRead( IN struct _DIMM *pDimm, IN UINT32 OutputSize, IN UINT32 MaxChunkSize, IN UINT32 Timeout OPTIONAL, IN DCPMM_FIS_INTERFACE DcpmmInterface, IN OUT UINT8 *pOutput, OUT UINT8 *pStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DCPMM_FIS_INPUT *pLargeInputPayload = NULL; DCPMM_FIS_OUTPUT *pLargeOutputPayload = NULL; UINT32 Offset = 0; UINT32 CurrentChunkSize = MaxChunkSize < OutputSize ? MaxChunkSize : OutputSize; NVDIMM_ENTRY(); if (OutputSize <= 0 || pDimm == NULL || pOutput == NULL || pStatus == NULL) { goto Finish; } pLargeInputPayload = (DCPMM_FIS_INPUT *)AllocateZeroPool(sizeof(*pLargeInputPayload)); if (pLargeInputPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pLargeOutputPayload = (DCPMM_FIS_OUTPUT *)AllocateZeroPool( sizeof(*pLargeOutputPayload) + CurrentChunkSize ); if (pLargeOutputPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pLargeInputPayload->Head.FisCmd = (UINT16)FIS_CMD_READ_LP_OUTPUT_MB; pLargeInputPayload->Head.DataSize = sizeof(pLargeInputPayload->Data.LpRead); pLargeInputPayload->Data.LpRead.Size = MaxChunkSize; for (Offset = 0; Offset < OutputSize; Offset += MaxChunkSize) { pLargeInputPayload->Data.LpRead.Offset = Offset; if (OutputSize - Offset < MaxChunkSize) { CurrentChunkSize = OutputSize - Offset; pLargeInputPayload->Data.LpRead.Size = CurrentChunkSize; } pLargeOutputPayload->Head.DataSize = CurrentChunkSize; ReturnCode = gNvmDimmData->pDcpmmProtocol->DcpmmFisRequest( DcpmmInterface, pDimm->DeviceHandle.AsUint32, pLargeInputPayload, pLargeOutputPayload, Timeout, pStatus ); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Error detected when sending DcpmmFisRequest command (RC = " FORMAT_EFI_STATUS ")", ReturnCode); goto Finish; } CopyMem_S(pOutput + Offset, CurrentChunkSize, &pLargeOutputPayload->Data.LpData, CurrentChunkSize); } Finish: FREE_POOL_SAFE(pLargeInputPayload); FREE_POOL_SAFE(pLargeOutputPayload); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif // !OS_BUILD ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Dimm.h000066400000000000000000002056611440615110200203740ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DIMM_H_ #define _DIMM_H_ #include #include #include #include #include #include #include #ifndef OS_BUILD #include #else typedef enum { PlaceHolder1, PlaceHolder2 } DCPMM_FIS_INTERFACE; #pragma pack(1) typedef struct { struct { UINT32 Data; } Junk; } DCPMM_FIS_INPUT; typedef struct { struct { UINT32 Data; } Junk; } DCPMM_FIS_OUTPUT; #pragma pack() #endif #ifdef OS_BUILD #define FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode) \ if (FW_ERROR(pFwCmd->Status)) { \ NVDIMM_ERR("Firmware cmd 0x%x:0x%x failed! FIS Error code: 0x%x", pFwCmd->Opcode, pFwCmd->SubOpcode, pFwCmd->Status); \ ReturnCode = MatchFwReturnCode(pFwCmd->Status); \ } \ else if (DSM_ERROR(pFwCmd->DsmStatus)) { \ NVDIMM_ERR("DSM for fw cmd 0x%x:0x%x failed! DSM Error code: 0x%x", pFwCmd->Opcode, pFwCmd->SubOpcode, pFwCmd->DsmStatus); \ ReturnCode = MatchDsmReturnCode(pFwCmd->DsmStatus); \ } #else #define FW_CMD_ERROR_TO_EFI_STATUS(pFwCmd, ReturnCode) \ if (FW_ERROR(pFwCmd->Status)) { \ NVDIMM_ERR("Firmware cmd 0x%x:0x%x failed! FIS Error code: 0x%x", pFwCmd->Opcode, pFwCmd->SubOpcode, pFwCmd->Status); \ ReturnCode = MatchFwReturnCode(pFwCmd->Status); \ } #endif //---> Turn on/off large payload support #define USE_LARGE_PAYLOAD //<--- #define DB_SHIFT 48 #define DB_SHIFT_32 48-32 //!< DB_SHIFT in UINT32 half of command register #define SUB_OP_SHIFT 40 #define OP_SHIFT 32 #define SQ_SHIFT 63 #define EXT_SUB_OP_SHIFT 8 #define MAX_SMALL_OUTPUT_REG_COUNT 32 #define BOOT_STATUS_REGISTER_OFFSET 0x20000 #define BOOT_STATUS_REGISTER_LENGTH 0x8 #define BW_STATUS_REGISTER_LENGTH 4 #define BW_APERTURE_LENGTH 8192 #define BW_DPA_MASK 0x1FFFFFFFFF #define BW_DPA_RIGHT_SHIFT 6 #define BW_LENGTH_MASK 0xff #define BW_LENGTH_POSITION 37 #define BW_OPERATION_MASK 0x1 #define BW_OPERATION_POSITION 45 #define BW_PENDING_MASK 0x80000000 #define BW_INVALID_ADDRESS_MASK 1 // 0b1 #define BW_ACCESS_ERROR (1 << 1) // 0b10 #define BW_PM_ACCESS_ERROR (1 << 4) // 0b10000 #define BW_REGION_ACCESS_ERROR (1 << 5) // 0b100000 #define PT_LONG_TIMEOUT_INTERVAL EFI_TIMER_PERIOD_MILLISECONDS(150) #define PT_UPDATEFW_TIMEOUT_INTERVAL EFI_TIMER_PERIOD_SECONDS(4) #define DEBUG_LOG_PAYLOAD_TYPE_LARGE 0 #define DEBUG_LOG_PAYLOAD_TYPE_SMALL 1 // // Translate between the NFIT device handle node/socket pair and an absolute socket index // The 4 bit Socket ID field allows a maximum of 16 sockets per node // #define NFIT_SOCKETS_PER_NODE 16 #define SOCKET_INDEX_TO_NFIT_SOCKET_ID(_skt) (_skt % NFIT_SOCKETS_PER_NODE) #define SOCKET_INDEX_TO_NFIT_NODE_ID(_skt) (_skt / NFIT_SOCKETS_PER_NODE) #define NFIT_NODE_SOCKET_TO_SOCKET_INDEX(_nodeId, _socketId) ((_nodeId * NFIT_SOCKETS_PER_NODE) + (_socketId)) typedef enum _BW_COMMAND_CODE { BwRead = 0, BwWrite = 1 } BW_COMMAND_CODE; /* DIMM STRUCTS */ struct _PMEM_DEV; struct _DIMM_REGION; /** Making a guess that all DIMMs must use a volume label space **/ typedef struct _LABEL_INFO { LIST_ENTRY LabelNode; UINT64 Signature; struct VOLUME_LABEL *pLabel; /** Offset from start of volume label space to the first copy of the volume label **/ UINT64 PrimaryLabelOffset; /** Offset from the start of the volume label space to the second copy of the volume label **/ UINT64 SecondaryLabelOffset; } LABEL_INFO; #define LABEL_INFO_SIGNATURE SIGNATURE_64('L', 'A', 'B', 'L', 'I', 'N', 'F', 'O') #define LABEL_INFO_FROM_NODE(a) CR(a, LABEL_INFO, LabelNode, LABEL_INFO_SIGNATURE) /** Mailbox Status Codes **/ enum { MB_SUCCESS = 0x00, //!< Command Complete MB_INVALID_PARAM = 0x01, //!< Input parameter invalid MB_DATA_TRANS_ERR = 0x02, //!< Error in the data transfer MB_INTERNAL_ERR = 0x03, //!< Internal device error MB_UNSUPPORTED_CMD = 0x04, //!< Opcode or Sub opcode not supported MB_BUSY = 0x05, //!< Device is busy processing a long operation MB_PASSPHRASE_ERR = 0x06, //!< Incorrect Passphrase MB_SECURITY_ERR = 0x07, //!< Security check on the image has failed MB_INVALID_STATE = 0x08, //!< Op not permitted in current security state MB_SYS_TIME_ERR = 0x09, //!< System time has not been set yet MB_DATA_NOT_SET = 0x0A //!< Get data called without ever calling set data }; typedef enum { LSA_OK, LSA_CORRUPTED, LSA_COULD_NOT_INIT, LSA_CORRUPTED_AFTER_INIT, LSA_COULD_NOT_READ_NAMESPACES, LSA_NOT_INIT } LsaStatus; #define OS_BW_CONTROL_REG_BASE (0x00000000) #define OS_BW_BLOCK_APERTURE_BASE (0x07800000) #define OS_BW_CTRL_REG_OFFSET (0x00000000) #define OS_BW_STATUS_REG_OFFSET (0x00000008) #define OS_BW_REG_SKIP (0x1000) #define OS_BW_APT_SKIP (0x2000) typedef struct _BLOCK_WINDOW { volatile UINT64 *pBwCmd; //!< Variable Amount of the command register volatile UINT32 *pBwStatus; //!< Variable Amount of the status register volatile VOID **ppBwApt; //!< Variable Amount of the aperture segment UINT32 LineSizeOfApt; //!< Line size of the interleaved aperture UINT32 NumSegmentsOfApt; //!< Number of segments of the interleaved aperture } BLOCK_WINDOW; typedef struct _DIMM { LIST_ENTRY DimmNode; UINT64 Signature; UINT16 DimmID; //!< SMBIOS Type 17 handle corresponding to this memory device /** Topology related fields **/ BOOLEAN InNfit; //!< True if Dimm is in NFIT SMBUS_DIMM_ADDR SmbusAddress; //!< SMBUS address NfitDeviceHandle DeviceHandle; UINT16 SocketId; UINT16 ImcId; UINT16 NodeControllerID; UINT16 ChannelId; UINT16 ChannelPos; UINT16 VendorId; //!< To allow loading of vendor specific driver UINT16 DeviceId; //!< To allow vendor to comprehend multiple devices UINT16 SubsystemVendorId; //!< Vendor identifier of memory subsystem controller UINT16 SubsystemDeviceId; //!< Device identifier of memory subsystem controller UINT16 Manufacturer; BOOLEAN ManufacturingInfoValid; //!< Validity of manufacturing location and date UINT8 ManufacturingLocation; UINT16 ManufacturingDate; UINT32 SerialNumber; CHAR8 PartNumber[PART_NUMBER_LEN]; UINT16 Rid; //!< Revision ID UINT16 SubsystemRid; //!< Revision ID of the subsystem memory controller from NFIT SKU_INFORMATION SkuInformation; /** Format interface code: Allows vendor hardware to be handled by a generic driver (behaves similar to class code in PCI) **/ UINT16 FmtInterfaceCode[MAX_IFC_NUM]; UINT32 FmtInterfaceCodeNum; FIRMWARE_VERSION FwVer; //!< Struct containing firmware version details UINT64 RawCapacity; //!< PM + volatile UINT64 VolatileStart; //!< DPA start of the Volatile region UINT64 VolatileCapacity; //!< Capacity in bytes mapped as volatile memory UINT64 PmStart; //!< DPA start of the PM region UINT64 PmCapacity; //!< DIMM Capacity (Bytes) to reserve for PM UINT64 InaccessibleVolatileCapacity; //!< Capacity in bytes for use as volatile memory that has not been exposed UINT64 InaccessiblePersistentCapacity; //!< Capacity in bytes for use as persistent memory that has not been exposed /** Insert only mapped/healthy DimmRegions into pDimm->pIsRegions array. PCD is not updated by BIOS on non-functional DIMMS. So non-functional DIMMs need to be excluded to avoid false indication of being in configured state. **/ struct _DIMM_REGION *pIsRegions[MAX_IS_PER_DIMM]; UINT32 IsRegionsNum; struct _DIMM_REGION *pIsRegionsNfit[MAX_IS_PER_DIMM]; UINT32 IsRegionsNfitNum; UINT8 IsNew; //!< if is incorporated with the rest of the DIMMs in the system UINT8 RebootNeeded; //!< Whether or not reboot is required to reconfigure dimm UINT8 LsaStatus; //!< The status of the LSA partition parsing for this DIMM BLOCK_WINDOW *pBw; NvDimmRegionMappingStructure *pRegionMappingStructure; //!< ptr to the table used to configure the mailbox SpaRangeTbl *pCtrlSpaTbl; //!> ptr to the spa range table associated with the mailbox table NvDimmRegionMappingStructure *pBlockDataRegionMappingStructure; //!< ptr to the table used to configure the block windows SpaRangeTbl *pBlockDataSpaTbl; //!< ptr to the spa range table associated with the block windows table UINT64 *pFlushAddress; //!< address to which data needs to be written to perform a WPQ flush BOOLEAN FlushRequired; //!< The boolean value indicating when the aperture needs to be flushed before IO BOOLEAN ControlWindowLatch; BOOLEAN EncryptionEnabled; //!< True if the DIMMs security is enabled UINT16 NvDimmStateFlags; /** Current regions config **/ BOOLEAN Configured; //!< true if the DIMM is configured UINT64 MappedVolatileCapacity; UINT64 MappedPersistentCapacity; /** Insert only mapped/healthy regions into pDimm->pISs array. PCD is not updated by BIOS on non-functional DIMMS. So non-functional DIMMs need to be excluded to avoid false indication of being in configured state. **/ struct _NVM_IS *pISs[MAX_IS_PER_DIMM]; UINT32 ISsNum; UINT8 ConfigStatus; //!< Configuration Status code struct _NVM_IS *pISsNfit[MAX_IS_PER_DIMM]; UINT32 ISsNfitNum; UINT32 PcdOemPartitionSize; UINT32 PcdLsaPartitionSize; /** Goal regions config **/ BOOLEAN RegionsGoalConfig; UINT64 VolatileSizeGoal; //!< Active only if RegionsGoalConfig is TRUE struct _REGION_GOAL *pRegionsGoal[MAX_IS_PER_DIMM]; //!< Active only if RegionsGoalConfig is TRUE UINT32 RegionsGoalNum; //!< Active only if RegionsGoalConfig is TRUE BOOLEAN PcdSynced; //!< Active only if RegionsGoalConfig is TRUE UINT8 GoalConfigStatus; //!< Active only if RegionsGoalConfig is TRUE VOID *pPcdLsa; // Always allocated to be size of PCD_OEM_PARTITION_INTEL_CFG_REGION_SIZE VOID *pPcdOem; UINT32 PcdOemSize; UINT16 ControllerRid; //!< Revision ID of the subsystem memory controller from FIS // If the dimm was declared non-functional during our driver initialization BOOLEAN NonFunctional; DIMM_BSR Bsr; UINT16 BootStatusBitmask; /* A pointer to a cached copy of the LABEL_STORAGE_AREA for this DIMM. This is only used during namespace initialization so it doesn't need to be repeatedly reloaded. It should not be considered current outside of initialization. */ LABEL_STORAGE_AREA *pLsa; #ifdef OS_BUILD /** Flag to indicate that ConfigStatus, IsNew, Configured, MappedVolatileCapacity & MappedPersistentCapacity values have been initialized using PCD in OS. **/ BOOLEAN PcdMappedMemInfoRead; #endif UINT8 FwActiveApiVersionMajor; //!< Specifies the FW Active Api major version UINT8 FwActiveApiVersionMinor; //!< Specifies the FW Active Api minor version BOOLEAN MixedSKUOffender; } DIMM; #define DIMM_SIGNATURE SIGNATURE_64('\0', '\0', '\0', '\0', 'D', 'I', 'M', 'M') #define DIMM_FROM_NODE(a) CR(a, DIMM, DimmNode, DIMM_SIGNATURE) #define MEMMAP_RANGE_UNDEFINED 1 #define MEMMAP_RANGE_RESERVED 2 #define MEMMAP_RANGE_VOLATILE 3 #define MEMMAP_RANGE_PERSISTENT 4 #define MEMMAP_RANGE_IS 5 #define MEMMAP_RANGE_IS_NOT_INTERLEAVED 6 #define MEMMAP_RANGE_APPDIRECT_NAMESPACE 10 #define MEMMAP_RANGE_FREE 11 #define MEMMAP_RANGE_LAST_USABLE_DPA 12 typedef enum { FreeCapacityForPersistentRegion = 0, FreeCapacityForADMode = 6 } FreeCapacityType; typedef struct _MEMMAP_RANGE { UINT64 Signature; LIST_ENTRY MemmapNode; DIMM *pDimm; UINT16 RangeType; UINT64 RangeStartDpa; UINT64 RangeLength; } MEMMAP_RANGE; #define MEMMAP_RANGE_SIGNATURE SIGNATURE_64('M', 'M', 'A', 'P', 'R', 'N', 'G', 'E') #define MEMMAP_RANGE_FROM_NODE(a) CR(a, MEMMAP_RANGE, MemmapNode, MEMMAP_RANGE_SIGNATURE) #define DISABLE_ARS_TOTAL_TIMEOUT_SEC 2 #define POLL_ARS_LONG_OP_DELAY_US 100000 //100ms delay between calls to retrieve long op #define MAX_FW_UPDATE_RETRY_ON_DEV_BUSY 10 // Account for ARS potentially getting restarted a few times in the background #define DSM_RETRY_SUGGESTED 0x5 // All possible combinations of transport and mailbox size typedef enum _DIMM_PASSTHRU_METHOD { DimmPassthruDdrtLargePayload = 0, DimmPassthruDdrtSmallPayload = 1, DimmPassthruSmbusSmallPayload = 2 } DIMM_PASSTHRU_METHOD; #ifdef OS_BUILD #define INI_PREFERENCES_LARGE_PAYLOAD_DISABLED L"LARGE_PAYLOAD_DISABLED" /* * Function get the ini configuration only on the first call * * It returns TRUE in case of large payload access is disabled and FALSE otherwise */ BOOLEAN ConfigIsLargePayloadDisabled(); #define INI_PREFERENCES_DDRT_PROTOCOL_DISABLED L"DDRT_PROTOCOL_DISABLED" /* * Function get the ini configuration only on the first call * * It returns TRUE in case of DDRT protocol access is disabled and FALSE otherwise */ BOOLEAN ConfigIsDdrtProtocolDisabled(); #endif // OS_BUILD EFI_STATUS DimmInit( IN struct _PMEM_DEV *pDev ); EFI_STATUS DimmExit( IN struct _PMEM_DEV *pDev ); EFI_STATUS InitializeDimmInventory( IN OUT struct _PMEM_DEV *pDev ); EFI_STATUS RemoveDimmInventory( IN OUT struct _PMEM_DEV *pDev ); /** Get dimm by Dimm ID Scan the dimm list for a dimm identified by Dimm ID @param[in] DimmID: The SMBIOS Type 17 handle of the dimm @param[in] pDimms: The head of the dimm list @retval DIMM struct pointer if matching dimm has been found @retval NULL pointer if not found **/ DIMM * GetDimmByPid( IN UINT32 DimmID, IN LIST_ENTRY *pDimms ); /** Get dimm by Dimm Device Handle as UINT32 Scan the dimm list for a dimm identified by Dimm device handle @param[in] DimmID: UINT32 device handle of the dimm @param[in] pDimms: The head of the dimm list @retval DIMM struct pointer if matching dimm has been found @retval NULL pointer if not found **/ DIMM * GetDimmByHandle( IN UINT32 DeviceHandle, IN LIST_ENTRY *pDimms ); /** Get dimm by serial number Scan the dimm list for a dimm identified by serial number @param[in] pDimms The head of the dimm list @param[in] DimmID The serial number of the dimm @retval DIMM struct pointer if matching dimm has been found @retval NULL pointer if not found **/ DIMM * GetDimmBySerialNumber( IN LIST_ENTRY *pDimms, IN UINT32 SerialNumber ); /** Get dimm by its unique identifier structure Scan the dimm list for a dimm identified by its unique identifier structure @param[in] pDimms The head of the dimm list @param[in] DimmUniqueId The unique identifier structure of the dimm @retval DIMM struct pointer if matching dimm has been found @retval NULL pointer if not found **/ DIMM * GetDimmByUniqueIdentifier( IN LIST_ENTRY *pDimms, IN DIMM_UNIQUE_IDENTIFIER DimmUniqueId ); // TODO: Remove, only added this for debug VOID PrintDimmMemmap( IN LIST_ENTRY *pMemmap ); /** Display memory map list. Use for debug purposes only @param[in] pDimm DIMM for which list the memory map **/ VOID ShowDimmMemmap( IN DIMM *pDimm ); DIMM * GetDimmByIndex( IN INT32 Index, IN struct _PMEM_DEV *pDev ); /** Get max Dimm ID Scan the dimm list for a max Dimm ID @param[in] pDimms: The head of the dimm list @retval Max Dimm ID or 0 if not found **/ UINT16 GetMaxPid( IN LIST_ENTRY *pDimms ); /** Retrieve list of memory regions of a DIMM Regions will be delivered in a form of sorted linked list with items containing start DPA and length of free ranges and they may overlap. Last item on the list will be a last DPA marker in order to point address boundary. @param[in] pDimm Target DIMM structure pointer @param[out] pMemmap Initialized list head to which region items will be added @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_OUT_OF_RESOURCES Not enough free space on target @retval EFI_SUCCESS List correctly retrieved **/ EFI_STATUS GetDimmMemmap( IN DIMM *pDimm, OUT LIST_ENTRY *pMemmap ); /** Retrieve list of free regions of a DIMM based on capacity type Free regions will be delivered in a form of sorted linked list with items containing start DPA and length of free ranges and they don't overlap each other @param[in] pDimm Target DIMM structure pointer @param[in] FreeCapacityTypeArg Determine a type of free capacity @param[out] pFreemap Initialized list head to which region items will be added @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_OUT_OF_RESOURCES Not enough free space on target @retval EFI_SUCCESS List correctly retrieved **/ EFI_STATUS GetDimmFreemap( IN DIMM *pDimm, IN FreeCapacityType FreeCapacityTypeArg, OUT LIST_ENTRY *pFreemap ); /** Free resources of memmap list items @param[in, out] pMemmapList Memmap list that items will be freed for **/ VOID FreeMemmapItems( IN OUT LIST_ENTRY *pMemmapList ); /** Merge overlapped ranges Memmap ranges may overlap each other. This function merges overlapped ranges to continuous ranges. Input list has to be sorted by DPA start address. Returned list will be sorted as well. The caller is responsible for a memory deallocation of the returned list. @param[in] pMemmapList Initialized list of ranges to merge. @param[out] pMergedList Initialized, output list to fill with continuous ranges. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS MergeMemmapItems( IN LIST_ENTRY *pMemmapList, OUT LIST_ENTRY *pMergedList ); /** Find free ranges Take list of usable ranges and subtract occupied ranges. The result will be list of free ranges. Input lists have to be sorted by DPA start address. Returned list will be sorted as well. The caller is responsible for a memory deallocation of the returned list. @param[in] pUsableRangesList Initialized list of usable ranges. @param[in] pOccupiedRangesList Initialized list of occupied ranges to subtract. @param[out] pFreeRangesList Initialized, output list to fill with free ranges. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS FindFreeRanges( IN LIST_ENTRY *pUsableRangesList, IN LIST_ENTRY *pOccupiedRangesList, OUT LIST_ENTRY *pFreeRangesList ); /** Create DIMM Perform all functions needed for DIMM initialization this includes: setting up mailbox structure retrieving and recording security status retrieving and recording the FW version retrieving and recording partition information setting up block windows @param[in] pNewDimm: input dimm structure to populate @param[in] pFitHead: fully populated NVM Firmware Interface Table @param[in] pPmttHead: fully populated Platform Memory Topology Table @param[in] Pid: SMBIOS Dimm ID of the DIMM to create @retval EFI_SUCCESS - Success @retval EFI_OUT_OF_RESOURCES - AllocateZeroPool failure @retval EFI_DEVICE_ERROR - Other errors **/ EFI_STATUS InitializeDimm ( IN DIMM *pNewDimm, IN ParsedFitHeader *pFitHead, IN ParsedPmttHeader *pPmttHead, IN UINT16 Pid ); /** Check if the DIMM containing the specified DIMM ID is manageable by the driver @param[in] UINT16 Dimm ID to check @retval BOOLEAN whether or not dimm is manageable **/ BOOLEAN IsDimmIdManageable( IN UINT16 DimmID ); /** This function performs a DIMM information refresh through the DIMM Information FV command. @param[in,out] pDimm the DIMM that we want to refresh. @retval EFI_SUCCESS - the DIMM was refreshed successfully. @retval EFI_INVALID_PARAMETER - pDimm is NULL. @retval EFI_OUT_OF_RESOURCES - the memory allocation failed. **/ EFI_STATUS RefreshDimm( IN OUT DIMM *pDimm ); EFI_STATUS RemoveDimm( OUT DIMM *pDimm, IN INT32 Force ); /** Retrieve DIMM Partition Info Send a FW command to retrieve the partition info of the DIMM Update the Intel NVM Dimm with pm start, pm capacity, and pm locality @param[in] pDimm: The Intel NVM Dimm to gather information from @retval Error Code? **/ EFI_STATUS GetDimmPartitionInfo( IN DIMM *pDimm ); EFI_STATUS ReadLabels( IN DIMM *pDimm, OUT LIST_ENTRY *pList ); /** Flushes date from the iMC buffers to the DIMM. The flushing is made by writing to the Flush Hint addresses. If there is no Flush Hint Table for the provided DIMM, The assumption is made that WPQ flush is not supported and not required. @param[in] pDimm: DIMM to flush the data into. **/ VOID DimmWPQFlush( IN DIMM *pDimm ); VOID FreeDimm( OUT DIMM *pDimm ); /** Parse Firmware Version Parse the FW version returned by the FW into a CPU format FW Payload has the FW version encoded in a binary coded decimal format @param[in] Fwr - Firmware revision in BCD format @retval Parsed firmware version as friendly FIRMWARE_VERSION structure **/ FIRMWARE_VERSION ParseFwVersion( IN UINT8 Fwr[FW_BCD_VERSION_LEN] ); /** Parse the BCD formatted FW API version into major and minor @param[out] pDimm @param[in] pPayload **/ VOID ParseFwApiVersion( OUT DIMM *pDimm, IN PT_ID_DIMM_PAYLOAD *pPayload ); /** Firmware command get security info Execute a FW command to check the security status of a DIMM @param[in] pDimm: The DIMM to retrieve security info on @param[out] pSecurityPayload: Area to place the security info returned from FW @param[in] DimmId: The SMBIOS table type 17 handle of the Intel NVM Dimm @retval EFI_SUCCESS: Success @retval EFI_OUT_OF_RESOURCES: memory allocation failure @retval Various errors from FW are still TBD **/ EFI_STATUS FwCmdGetSecurityInfo( IN DIMM *pDimm, OUT PT_GET_SECURITY_PAYLOAD *pSecurityPayload ); /** Is Firmware command get security Opt-In supported Get security opt-in command is supported for certain fw versions with certain opt-in codes @param[in] pDimm: The DIMM to send get security opt-in command @param[in] OptInCode: Opt-In Code that is requested status for @retval BOOLEAN: return if the command is supported for opt-in code **/ BOOLEAN IsGetSecurityOptInSupported( IN DIMM *pDimm, IN UINT16 OptInCode ); /** Firmware command get security Opt-In Execute a FW command to check the security Opt-In code of a DIMM @param[in] pDimm: The DIMM to retrieve security info on @param[in] OptInCode: Opt-In Code that is requested status for @param[out] pSecurityOptIn: Area to place the returned from FW @retval EFI_SUCCESS: Success @retval EFI_OUT_OF_RESOURCES: memory allocation failure @retval Various errors from FW are still TBD **/ EFI_STATUS FwCmdGetSecurityOptIn( IN DIMM *pDimm, IN UINT16 OptInCode, OUT PT_OUTPUT_PAYLOAD_GET_SECURITY_OPT_IN *pSecurityOptIn ); /** Firmware command to retrieve the ARS status of a particular DIMM. @param[in] pDimm Pointer to the DIMM to retrieve ARSStatus on @param[out] pDimmARSStatus Pointer to the individual DIMM ARS status @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval Various errors from FW **/ EFI_STATUS FwCmdGetARS( IN DIMM *pDimm, OUT UINT8 *pDimmARSStatus ); /** Firmware command to disable ARS @param[in] pDimm Pointer to the DIMM to retrieve ARSStatus on @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval Various errors from FW **/ EFI_STATUS FwCmdDisableARS( IN DIMM *pDimm ); /** Firmware command to get Error logs Small and large payloads are optional, but at least one has to be provided. @param[in] pDimm Target DIMM structure pointer @param[in] pInputPayload - filled input payload @param[out] pOutputPayload - small payload result data of get error log operation @param[in] OutputPayloadSize - size of small payload @param[out] pLargeOutputPayload - large payload result data of get error log operation @param[in] LargeOutputPayloadSize - size of large payload @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetErrorLog ( IN DIMM *pDimm, IN PT_INPUT_PAYLOAD_GET_ERROR_LOG *pInputPayload, OUT VOID *pOutputPayload OPTIONAL, IN UINT32 OutputPayloadSize OPTIONAL, OUT VOID *pLargeOutputPayload OPTIONAL, IN UINT32 LargeOutputPayloadSize OPTIONAL ); /** Firmware command to get Command Effect Log Entries @param[in] pDimm Target DIMM structure pointer @param[out] ppLogEntry A pointer to the CEL entry table for a given PMem module. @param[out] pEntryCount The number of CEL entries @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetCommandEffectLog( IN DIMM *pDimm, OUT COMMAND_EFFECT_LOG_ENTRY **ppLogEntry, OUT UINT32 *pEntryCount ); /** Firmware command to get a specified debug log @param[in] pDimm Target DIMM structure pointer @param[in] LogSource Debug log source buffer to retrieve @param[out] ppDebugLogBuffer - an allocated buffer containing the raw debug logs @param[out] pDebugLogBufferSize - the size of the raw debug log buffer @param[out] pCommandStatus structure containing detailed NVM error codes Note: The caller is responsible for freeing the returned buffers @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetFwDebugLog ( IN DIMM *pDimm, IN UINT8 LogSource, OUT VOID **ppDebugLogBuffer, OUT UINTN *pDebugLogBufferSize, OUT COMMAND_STATUS *pCommandStatus ); /** Firmware command to get debug logs size in MB @param[in] pDimm Target DIMM structure pointer @param[out] pLogSizeInMb - number of MB of Logs to be fetched @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetFwDebugLogSize( IN DIMM *pDimm, OUT UINT64 *pLogSizeInMb ); /** Firmware command Identify DIMM. Execute a FW command to get information about DIMM. @param[in] pDimm The Intel NVM Dimm to retrieve identify info on @param[out] pPayload Area to place the identity info returned from FW @retval EFI_SUCCESS: Success @retval EFI_OUT_OF_RESOURCES: memory allocation failure **/ EFI_STATUS FwCmdIdDimm( IN DIMM *pDimm, OUT PT_ID_DIMM_PAYLOAD *pPayload ); /** Firmware command Device Characteristics @param[in] pDimm The Intel NVM Dimm to retrieve device characteristics info for @param[out] ppPayload Area to place returned info from FW The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more input parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS FwCmdDeviceCharacteristics ( IN DIMM *pDimm, OUT PT_DEVICE_CHARACTERISTICS_OUT **ppPayload ); /** Firmware command access/read Platform Config Data using small payload only. The function allows to specify the requested data offset and the size. The function is going to allocate the ppRawData buffer if it is not allocated. The buffer's minimal size is the size of the Partition! @param[in] pDimm The Intel NVM Dimm to retrieve identity info on @param[in] PartitionId Partition number to get data from @param[in] ReqOffset Data read starting point @param[in] ReqDataSize Number of bytes to read @param[out] Pointer to the buffer pointer for storing retrieved data @retval EFI_SUCCESS Success @retval Error code **/ EFI_STATUS FwGetPCDFromOffsetSmallPayload( IN DIMM *pDimm, IN UINT8 PartitionId, IN UINT32 ReqOffset, IN UINT32 ReqDataSize, OUT UINT8 **ppRawData); /** Firmware command get Platform Config Data. Execute a FW command to get information about DIMM regions and REGIONs configuration. The caller is responsible for a memory deallocation of the ppPlatformConfigData @param[in] pDimm The Intel NVM Dimm to retrieve identity info on @param[in] PartitionId Partition number to get data from @param[out] ppRawData Pointer to a new buffer pointer for storing retrieved data @param[out] pDataSize Pointer to the retrieved data buffer size @retval EFI_SUCCESS: Success @retval EFI_OUT_OF_RESOURCES: memory allocation failure **/ EFI_STATUS FwCmdGetPlatformConfigData( IN DIMM *pDimm, IN UINT8 PartitionId, OUT UINT8 **ppRawData ); /** Firmware command to get the PCD size @param[in] pDimm The target DIMM @param[in] PartitionId The partition ID of the PCD @param[out] pPcdSize Pointer to the PCD size @retval EFI_INVALID_PARAMETER Invalid parameter passed @retval EFI_OUT_OF_RESOURCES Could not allocate memory @retval EFI_SUCCESS Command successfully run **/ EFI_STATUS FwCmdGetPlatformConfigDataSize ( IN DIMM *pDimm, IN UINT8 PartitionId, OUT UINT32 *pPcdSize ); /** Firmware command access/write Platform Config Data using small payload only. The function allows to specify the requested data offset and the size. The buffer's minimal size is the size of the Partition! @param[in] pDimm The Intel NVM Dimm to send Platform Config Data to @param[in] PartitionId Partition number for data to be send to @param[in] pRawData Pointer to a data buffer that will be sent to the DIMM @param[in] ReqOffset Data write starting point @param[in] ReqDataSize Number of bytes to write @retval EFI_SUCCESS Success @retval Error code **/ EFI_STATUS FwSetPCDFromOffsetSmallPayload( IN DIMM *pDimm, IN UINT8 PartitionId, IN UINT8 *pRawData, IN UINT32 ReqOffset, IN UINT32 ReqDataSize); /** Firmware command set Platform Config Data. Execute a FW command to send REGIONs configuration to the Platform Config Data. @param[in] pDimm The Intel NVM Dimm to send Platform Config Data to @param[in] PartitionId Partition number for data to be send to @param[in] pRawData Pointer to a data buffer that will be sent to the DIMM @param[in] RawDataSize Size of pRawData in bytes @retval EFI_SUCCESS: Success @retval EFI_OUT_OF_RESOURCES: memory allocation failure **/ EFI_STATUS FwCmdSetPlatformConfigData( IN DIMM *pDimm, IN UINT8 PartitionId, IN UINT8 *pRawData, IN UINT32 RawDataSize ); /** Firmware command to get Alarm Thresholds @param[in] pDimm The Intel NVM Dimm to retrieve Alarm Thresholds @param[out] ppPayloadAlarmThresholds Area to place the Alarm Thresholds data returned from FW. The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadAlarmThresholds is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetAlarmThresholds( IN DIMM *pDimm, OUT PT_PAYLOAD_ALARM_THRESHOLDS **ppPayloadAlarmThresholds ); /** Firmware command to set Alarm Thresholds @param[in] pDimm The Intel NVM Dimm to set Alarm Thresholds @param[in] ppPayloadAlarmThresholds Alarm Thresholds data to set @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadAlarmThresholds is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdSetAlarmThresholds( IN DIMM *pDimm, IN PT_PAYLOAD_ALARM_THRESHOLDS *pPayloadAlarmThresholds ); /** Runs and handles errors errors for firmware update over both large and small payloads. @param[in] pDimm Pointer to DIMM @param[in] pImageBuffer Pointer to fw image buffer @param[in] ImageBufferSize Size in bytes of fw image buffer @param[out] pNvmStatus Pointer to Nvm status variable to set on error @param[out] pCommandStatus optional structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdUpdateFw( IN DIMM *pDimm, IN CONST VOID *pImageBuffer, IN UINTN ImageBufferSize, OUT NVM_STATUS *pNvmStatus, OUT COMMAND_STATUS *pCommandStatus OPTIONAL ); /** Firmware command to get SMART and Health Info @param[in] pDimm The Intel NVM Dimm to retrieve SMART and Health Info @param[out] ppPayloadSmartAndHealth Area to place SMART and Health Info data The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadSmartAndHealth is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetSmartAndHealth( IN DIMM *pDimm, OUT PT_PAYLOAD_SMART_AND_HEALTH **ppPayloadSmartAndHealth ); /** Command to send a pass-through firmware command to retrieve a specified memory info page @param[in] pDimm Dimm to retrieve the specified memory info page from @param[in] PageNum The specific memory info page @param[in] PageSize The size of memory info page, which is 128 bytes @param[out] ppPayloadMemoryInfoPage Area to place the retrieved memory info page contents The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadMediaErrorsInfo is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetMemoryInfoPage ( IN DIMM *pDimm, IN CONST UINT8 PageNum, IN CONST UINT32 PageSize, OUT VOID **ppPayloadMemoryInfoPage ); /** Firmware command to get Firmware Image Info @param[in] pDimm Dimm to retrieve Firmware Image Info for @param[out] ppPayloadFwImage Area to place Firmware Image Info data The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadFwImage is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetFirmwareImageInfo ( IN DIMM *pDimm, OUT PT_PAYLOAD_FW_IMAGE_INFO **ppPayloadFwImage ); /** Firmware command to get Power Management Policy Info (for FIS 1.3+) @param[in] pDimm The Intel DCPMM to retrieve Power Management Policy Info @param[out] ppPayloadPowerManagementPolicy Area to place Power Management Policy Info data The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadPowerManagementPolicy is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetPowerManagementPolicy( IN DIMM *pDimm, OUT PT_POWER_MANAGEMENT_POLICY_OUT **ppPayloadPowerManagementPolicy ); #ifdef OS_BUILD /** Firmware command to get PMON Info @param[in] pDimm The DC PMEM Module to retrieve PMON Info @param[out] pPayloadPMONRegisters Area to place PMON Registers data The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or pPayloadPMONRegisters is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetPMONRegisters( IN DIMM *pDimm, IN UINT8 SmartDataMask, OUT PMON_REGISTERS *pPayloadPMONRegisters ); /** Firmware command to set PMON Info @param[in] pDimm The DC PMEM Module to retrieve PMON Info @param[out] PMONGroupEnable Specifies which PMON Group to enable. The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdSetPMONRegisters( IN DIMM *pDimm, IN UINT8 PMONGroupEnable ); #endif /** Firmware command to get package sparing policy @param[in] pDimm The Intel NVM Dimm to retrieve Package Sparing policy @param[out] ppPayloadPackageSparingPolicy Area to place Package Sparing policy data The caller is responsible to free the allocated memory with the FreePool function. @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDimm or ppPayloadPackageSparingPolicy is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetPackageSparingPolicy( IN DIMM *pDimm, OUT PT_PAYLOAD_GET_PACKAGE_SPARING_POLICY **ppPayloadPackageSparingPolicy ); /** Get long operation status FW command @param[in] pDimm Dimm to retrieve long operation status from @param[out] pFwStatus FW status returned by dimm. FW_INTERNAL_DEVICE_ERROR means there is no long operation currently @param[out] pLongOpStatus Filled payload with data @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS FwCmdGetLongOperationStatus( IN DIMM *pDimm, OUT UINT8 *pFwStatus, OUT PT_OUTPUT_PAYLOAD_FW_LONG_OP_STATUS *pLongOpStatus ); /** Execute Firmware command to Get DIMM Partition Info @param[in] pDimm The DIMM to retrieve security info on @param[out] pPayload Area to place the info returned from FW @retval EFI_INVALID_PARAMETER NULL pointer for DIMM structure provided @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval EFI_... Other errors from subroutines @retval EFI_SUCCESS Success **/ EFI_STATUS FwCmdGetDimmPartitionInfo( IN DIMM *pDimm, OUT PT_DIMM_PARTITION_INFO_PAYLOAD *pPayload ); /** Create and configure block window Create the block window structure. This includes locating each part of the block window in the system physical address space, and mapping each part into the virtual address space. @param[in, out] pDimm: DIMM to create the Bw for @param[in] PFitHead: Parsed Fit Head @param[in] pMbITbl: the interleave table for mailbox @param[in] pBwITbl: the interleave table for block window @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES in case of allocate memory error **/ EFI_STATUS EFIAPI CreateBw( IN OUT DIMM *pDimm, IN ParsedFitHeader *pFitHead, IN InterleaveStruct *pMbITbl OPTIONAL, IN InterleaveStruct *pBwITbl OPTIONAL ); /** Set Block Window Command to read/write operation @param[in] Dpa - Memory DPA @param[in] Length - The transfer size is the number of cache lines (Cache line = 64 bytes) @param[in] BwOperation - Read/Write command @param[out] pBwCommand - 64-bit Command Register buffer **/ VOID PrepareBwCommand( IN UINT64 Dpa, IN UINT8 Length, IN UINT8 BwOperation, OUT UINT64 *pCommand ); /** Check Block Input Parameters @param[in] pDimm: DIMM to check block window pointers @retval EFI_INVALID_PARAMETER if pDimm or some internal Block Window pointer is NULL (pBw, pBw->pBwCmd, pBw->pBwApt, pBw->pBwStatus) **/ EFI_STATUS EFIAPI CheckBlockInputParameters( IN DIMM *pDimm ); /** Poll Firmware Command Completion Poll the status register of the BW waiting for the status register complete bit to be set. @param[in] pDimm - Dimm with block window with submitted command @param[in] Timeout The timeout, in 100ns units, to use for the execution of the BW command. A Timeout value of 0 means that this function will wait infinitely for the command to execute. If Timeout is greater than zero, then this function will return EFI_TIMEOUT if the time required to execute the receive data command is greater than Timeout. @param[out] pStatus returned Status from BW status register @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR FW error received @retval EFI_TIMEOUT A timeout occurred while waiting for the command to execute. **/ EFI_STATUS EFIAPI CheckBwCmdTimeout( IN DIMM *pDimm, IN UINT64 Timeout, OUT UINT32 *pStatus ); /** Get command status from command status register @param[in] pDimm - pointer to DIMM with Block Window @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER in case of memory allocate error @retval other - error code matching to status register **/ EFI_STATUS EFIAPI GetBwCommandStatus( IN DIMM *pDimm ); /** Read a number of bytes from a DIMM @param[in] pDimm: DIMM to read from @param[in] Offset: offset from the start of the region this mem type uses @param[in] Nbytes: Number of bytes to read @param[out] pBuffer: Buffer to place bytes into @retval EFI_ACCESS_DENIED if BW request attempts to access a locked or disabled BW or PM region @retval EFI_DEVICE_ERROR If DIMM DPA address is invalid or uncorrectable access error occurred @retval EFI_INVALID_PARAMETER If pDimm, pBuffer or some internal BW pointer is NULL @retval EFI_TIMEOUT A timeout occurred while waiting for the command to execute. **/ EFI_STATUS EFIAPI ApertureRead( IN DIMM *pDimm, IN UINT64 Offset, IN UINT64 Nbytes, OUT CHAR8 *pBuffer ); /** Write a number of bytes to a DIMM @param[out] pDimm: DIMM to write to @param[in] Offset: offset from the start of the region this mem type uses @param[in] Nbytes: Number of bytes to write @param[in] pBuffer: Buffer containing data to write @retval EFI_ACCESS_DENIED if BW request attempts to access a locked or disabled BW or PM region @retval EFI_DEVICE_ERROR If DIMM DPA address is invalid or uncorrectable access error occurred @retval EFI_INVALID_PARAMETER If pDimm, pBuffer or some internal BW pointer is NULL @retval EFI_TIMEOUT A timeout occurred while waiting for the command to execute. **/ EFI_STATUS EFIAPI ApertureWrite( OUT DIMM *pDimm, IN UINT64 Offset, IN UINT64 Nbytes, IN CHAR8 *pBuffer ); /** Copy data from an interleaved buffer to a regular buffer. Both buffers have to be equal or greater than NumOfBytes. @param[out] pRegularBuffer output regular buffer @param[in] RegularBufferSz size of the RegularBuffer @param[in] ppInterleavedBuffer input interleaved buffer @param[in] LineSize line size of interleaved buffer @param[in] NumOfBytes number of bytes to copy **/ VOID ReadFromInterleavedBuffer( OUT VOID *pRegularBuffer, IN UINTN RegularBufferSz, IN VOID **ppInterleavedBuffer, IN UINT32 LineSize, IN UINT32 NumOfBytes ); /** Flush data from an interleaved buffer. The InterleavedBuffer needs to be at least NumOfBytes. @param[in] ppInterleavedBuffer input interleaved buffer @param[in] LineSize line size of interleaved buffer @param[in] NumOfBytes number of bytes to copy **/ VOID FlushInterleavedBuffer( IN VOID **ppInterleavedBuffer, IN UINT32 LineSize, IN UINT32 NumOfBytes ); /** Copies 'Length' no of bytes from source buffer into destination buffer The function attempts to perform an 8 byte copy and falls back to 1 byte copies if required @param[in] SourceBuffer Source address @param[in] Length The length in no of bytes @param[out] DestinationBuffer Destination address **/ VOID * CopyMem_8 ( IN OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length ); /** Copy data from a regular buffer to an interleaved buffer. Both buffers have to be equal or greater than NumOfBytes. @param[in] pRegularBuffer input regular buffer @param[out] ppInterleavedBuffer output interleaved buffer @param[in] LineSize line size of interleaved buffer @param[in] NumOfBytes number of bytes to copy **/ VOID WriteToInterleavedBuffer( IN VOID *pRegularBuffer, OUT VOID **ppInterleavedBuffer, IN UINT32 LineSize, IN UINT32 NumOfBytes ); /** Clear a part or whole of interleaved buffer. @param[out] ppInterleavedBuffer interleaved buffer to clear @param[in] LineSize line size of interleaved buffer @param[in] NumOfBytes number of bytes to clear **/ VOID ClearInterleavedBuffer( OUT VOID **ppInterleavedBuffer, IN UINT32 LineSize, IN UINT32 NumOfBytes ); /** Get Platform Config Data OEM partition Intel config region and check a correctness of header. We only return the actua PCD config data, from the first 64KiB of Intel FW/SW config metadata. The latter 64KiB is reserved for OEM use. The caller is responsible for a memory deallocation of the ppPlatformConfigData @param[in] pDimm The Intel NVM Dimm to retrieve PCD from @param[in] RestoreCorrupt If true will generate a default PCD when a corrupt header is found @param[out] ppPlatformConfigData Pointer to a new buffer pointer for storing retrieved data @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR Incorrect PCD header @retval Other return codes from GetPcdOemConfigDataUsingSmallPayload **/ EFI_STATUS GetPlatformConfigDataOemPartition( IN DIMM *pDimm, IN BOOLEAN RestoreCorrupt, OUT NVDIMM_CONFIGURATION_HEADER **ppPlatformConfigData ); /** Firmware command to get Partition Data using large payload. Execute a FW command to get information about DIMM regions and REGIONs configuration. The caller is responsible for a memory deallocation of the ppPlatformConfigData @param[in] pDimm The Intel NVM Dimm to retrieve identity info on @param[in] PartitionId Partition number to get data from @param[out] ppRawData Pointer to a new buffer pointer for storing retrieved data @retval EFI_SUCCESS: Success @retval EFI_OUT_OF_RESOURCES: memory allocation failure **/ EFI_STATUS FwCmdGetPcdLargePayload( IN DIMM *pDimm, IN UINT8 PartitionId, OUT UINT8 **ppRawData ); /** Set Platform Config Data OEM Partition Intel config region. We only write to the first 64KiB of Intel FW/SW config metadata. The latter 64KiB is reserved for OEM use. @param[in] pDimm The Intel NVM Dimm to set PCD @param[in] pNewConf Pointer to new config data to write @param[in] NewConfSize Size of pNewConf @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER NULL inputs or bad size @retval Other return codes from FwCmdSetPlatformConfigData **/ EFI_STATUS SetPlatformConfigDataOemPartition( IN DIMM *pDimm, IN NVDIMM_CONFIGURATION_HEADER *pNewConf, IN UINT32 NewConfSize ); /** Firmware command Get Viral Policy Execute a FW command to check the security status of a DIMM @param[in] pDimm The DIMM to retrieve viral policy @param[out] pViralPolicyPayload buffer to retrieve DIMM FW response @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Parameter supplied is invalid @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval Various errors from FW **/ EFI_STATUS FwCmdGetViralPolicy( IN DIMM *pDimm, OUT PT_VIRAL_POLICY_PAYLOAD *pViralPolicyPayload ); /** Payload is the same for set and get operation **/ EFI_STATUS FwCmdGetOptionalConfigurationDataPolicy( IN DIMM *pDimm, OUT PT_OPTIONAL_DATA_POLICY_PAYLOAD *pOptionalDataPolicyPayload ); /** Payload is the same for set and get operation **/ EFI_STATUS FwCmdSetOptionalConfigurationDataPolicy( IN DIMM *pDimm, IN PT_OPTIONAL_DATA_POLICY_PAYLOAD *pOptionalDataPolicyPayload ); /** Get error logs for given dimm parse it and save in common error log structure @param[in] pDimm - pointer to DIMM to get errors @param[in] ThermalError - is thermal error (if not it is media error) @param[in] HighLevel - high level if true, low level otherwise @param[in] SequenceNumber - sequence number of error to fetch in queue @param[in] MaxErrorsToSave - max number of new error entries that can be saved in output array @param[out] pErrorsFetched - number of new error entries saved in output array @param[out] pErrorLogs - output array of errors @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER if some pointer is NULL @retval other - error code matching to status register **/ EFI_STATUS GetAndParseFwErrorLogForDimm( IN DIMM *pDimm, IN CONST BOOLEAN ThermalError, IN CONST BOOLEAN HighLevel, IN CONST UINT16 SequenceNumber, IN UINT32 MaxErrorsToSave, OUT UINT32 *pErrorsFetched, OUT ERROR_LOG_INFO *pErrorLogs ); /** Get count of media and/or thermal errors on given DIMM @param[in] pDimm - pointer to DIMM to get registers for. @param[out] pMediaLogCount - number of media errors on DIMM @param[out] pThermalLogCount - number of thermal errors on DIMM @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS FwCmdGetErrorCount( IN DIMM *pDimm, OUT UINT32 *pMediaLogCount OPTIONAL, OUT UINT32 *pThermalLogCount OPTIONAL ); /** Matches FW return code to one of available EFI_STATUS EFI base types @param[in] Status - status byte returned from FW command @retval - Appropriate EFI_STATUS **/ EFI_STATUS MatchFwReturnCode ( IN UINT8 FwStatus ); #ifdef OS_BUILD /** Matches DSM return code to one of available EFI_STATUS EFI base types @param[in] DsmStatus - status byte returned from FW command @retval - Appropriate EFI_STATUS **/ EFI_STATUS MatchDsmReturnCode( IN UINT8 DsmStatus ); #endif /** Check if SKU conflict occurred. Any mixed modes between DIMMs are prohibited on a platform. @param[in] pDimm1 - first DIMM to compare SKU mode @param[in] pDimm2 - second DIMM to compare SKU mode @retval NVM_SUCCESS - if everything went fine @retval NVM_ERR_DIMM_SKU_MODE_MISMATCH - if mode conflict occurred @retval NVM_ERR_DIMM_SKU_SECURITY_MISMATCH - if security mode conflict occurred **/ NvmStatusCode IsDimmSkuModeMismatch( IN DIMM *pDimm1, IN DIMM *pDimm2 ); /** Calculate a size of capacity considered Reserved. It is the aligned PM capacity less the mapped AD capacity @param[in] Dimm to retrieve reserved size for @param[out] pReservedCapacity pointer to reserved capacity @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_ABORTED Failure to retrieve current memory mode @retval EFI_SUCCESS Success **/ EFI_STATUS GetReservedCapacity( IN DIMM *pDimm, OUT UINT64 *pReservedCapacity ); /** Transform temperature in FW format to usual integer in Celsius @param[in] Temperature Temperature from FW @retval Value in Celsius **/ INT16 TransformFwTempToRealValue( IN TEMPERATURE Temperature ); /** Transform temperature from usual integer in Celsius to FW format @param[in] Value Temperature in Celsius @retval Temperature in FW format **/ TEMPERATURE TransformRealValueToFwTemp( IN INT16 Value ); /** Get the Dimm UID (a globally unique NVDIMM identifier) for DIMM, as per the following representation defined in ACPI 6.1 specification: "%02x%02x-%02x-%02x%2x-%02x%02x%02x%02x" (if the Manufacturing Location and Manufacturing Date fields are valid) "%02x%02x-%02x%02x%02x%02x" (if the Manufacturing Location and Manufacturing Date fields are invalid) @param[in] pDimm DIMM for which the UID is being initialized @param[out] pDimmUid Array to store Dimm UID @param[in] DimmUidLen Size of pDimmUid @retval EFI_SUCCESS Dimm UID field was initialized successfully. @retval EFI_INVALID_PARAMETER one or more parameters are NULL. **/ EFI_STATUS GetDimmUid( IN DIMM *pDimm, OUT CHAR16 *pDimmUid, IN UINT32 DimmUidLen ); /** Set object status for DIMM @param[out] pCommandStatus Pointer to command status structure @param[in] pDimm DIMM for which the object status is being set @param[in] Status Object status to set **/ VOID SetObjStatusForDimm( OUT COMMAND_STATUS *pCommandStatus, IN DIMM *pDimm, IN NVM_STATUS Status ); /** Get overwrite DIMM operation status for DIMM @param[in] pDimm DIMM to retrieve overwrite DIMM operation status from @param[out] pOverwriteDimmStatus Retrieved status @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ EFI_STATUS GetOverwriteDimmStatus( IN DIMM *pDimm, OUT UINT8 *pOverwriteDimmStatus ); /** Get DIMM Info LongOp status based on FW LongOp status code @param[in] pDimm DIMM to retrieve overwrite DIMM operation status from @param[in] pOpcode Opcode of long op command @param[in] pOpcode Subopcode of long op command @param[out] pLongOpDimmInfoStatus Long Op status @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL or invalid **/ EFI_STATUS GetLongOpDimmInfoStatus( IN DIMM *pDimm, IN UINT8 Opcode, IN UINT8 SubOpcode, OUT UINT8 *pLongOpDimmInfoStatus ); /** Customer Format Dimm Send a customer format command through the smbus @param[in] pDimm The dimm to attempt to format @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Invalid FW Command Parameter **/ EFI_STATUS FwCmdFormatDimm( IN DIMM *pDimm ); /** Firmware command to get DDRT IO init info @param[in] pDimm Target DIMM structure pointer @param[out] pDdrtIoInitInfo pointer to filled payload with DDRT IO init info @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS FwCmdGetDdrtIoInitInfo( IN DIMM *pDimm, OUT PT_OUTPUT_PAYLOAD_GET_DDRT_IO_INIT_INFO *pDdrtIoInitInfo ); /** Get Command Access Policy for a specific command @param[IN] pDimm Target DIMM structure pointer @param[IN] Opcode for the command @param[IN] SubOpcode for the command @param[OUT] pRestricted TRUE if restricted, else FALSE @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS FwCmdGetCommandAccessPolicy( IN DIMM *pDimm, IN UINT8 Opcode, IN UINT8 Subopcode, OUT BOOLEAN *pRestricted ); /** Inject Temperature error payload @param[IN] pDimm Target DIMM structure pointer @param[IN] subopcode for error injection command @param[OUT] pInjectInputPayload - input payload to be sent @param[OUT] pFwStatus FW status returned by dimm @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS FwCmdInjectError( IN DIMM *pDimm, IN UINT8 SubOpcode, OUT void *pInjectInputPayload, OUT UINT8 *pFwStatus ); /** Firmware command to get DIMMs system time @param[in] pDimm Target DIMM structure pointer @param[out] pSystemTimePayload pointer to filled payload @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS FwCmdGetSystemTime( IN DIMM *pDimm, OUT PT_SYSTEM_TIME_PAYLOAD *pSystemTimePayload ); /** Firmware command to get extended ADR status info @param[in] pDimm Target DIMM structure pointer @param[out] pExtendedAdrInfo pointer to filled payload with extended ADR info @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if failed to open PassThru protocol @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null @retval EFI_UNSUPPORTED if FIS doesn't support Get Admin Features/Extended ADR **/ EFI_STATUS FwCmdGetExtendedAdrInfo( IN DIMM *pDimm, OUT PT_OUTPUT_PAYLOAD_GET_EADR *pExtendedAdrInfo ); /** Firmware command to get Latch System Shutdown State @param[in] pDimm Target DIMM structure pointer @param[out] pExtendedAdrInfo pointer to filled payload with Latch System Shutdown State info @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER if parameter provided is invalid @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdGetLatchSystemShutdownStateInfo( IN DIMM *pDimm, OUT PT_OUTPUT_PAYLOAD_GET_LATCH_SYSTEM_SHUTDOWN_STATE *pLastSystemShutdownStateInfo ); /** Helper function to run a simple small payload command. @param[in] pDimm Target DIMM structure pointer @param[in] Opcode Firmware command opcode to use @param[in] SubOpcode Firmware command sub-opcode to use @param[in] InputPayload Input payload buffer @param[in] InputPayloadSize Input payload buffer size @param[in] OutputPayload Output payload buffer @param[in] OutputPayloadSize Output payload buffer size @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER if parameter provided is invalid @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS FwCmdSmallPayload( IN DIMM *pDimm, IN UINT8 Opcode, IN UINT8 SubOpcode, IN UINT8 *InputPayload OPTIONAL, IN UINT8 InputPayloadSize, OUT UINT8 *OutputPayload OPTIONAL, IN UINT8 OutputPayloadSize ); /** Check if DIMM is manageable @param[in] pDimm the DIMM struct @retval BOOLEAN whether or not dimm is manageable **/ BOOLEAN IsDimmManageable( IN DIMM *pDimm ); /** Get number of manageable dimms @param[in] ppDimms the Dimms array @param[in] DimmCount the number of Dimms in ppDimms array @retval UINT16 the count of manageable dimms **/ UINT16 GetManageableDimmsCount( IN DIMM **ppDimms, IN CONST UINT16 DimmCount ); /** Check if DIMM is in supported config @param[in] pDimm the DIMM struct @retval BOOLEAN whether or not dimm is in supported config **/ BOOLEAN IsDimmInSupportedConfig( IN DIMM *pDimm ); /** Check if DIMM is in population violation @param[in] pDimm the DIMM struct @retval BOOLEAN whether or not dimm is in population violation **/ BOOLEAN IsDimmInPopulationViolation( IN DIMM *pDimm ); /** Check if DIMM is in population violation and fully unmapped @param[in] pDimm the DIMM struct @retval BOOLEAN whether or not dimm is in population violation and fully unmapped **/ BOOLEAN IsDimmInUnmappedPopulationViolation( IN DIMM *pDimm ); /** Check if DIMM is in population violation and persistent memory is still mapped @param[in] pDimm the DIMM struct @retval BOOLEAN whether or not dimm is in population violation and persistent memory is still mapped **/ BOOLEAN IsDimmInPmMappedPopulationViolation( IN DIMM *pDimm ); /** Check if PMem module is accessible through small payload based on the specified transport protocol @param[in] pDimm the DIMM struct @param[in] Protocol transport protocol to be used @param[out] pIsInterfaceReady whether or not specified dimm interface is ready @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL or invalid **/ EFI_STATUS GetDimmInterfaceStatus( IN DIMM *pDimm, IN TRANSPORT_PROTOCOL Protocol, OUT BOOLEAN *pIsInterfaceReady ); /** Check if the dimm interface code of this DIMM is supported @param[in] pDimm the DIMM struct @retval true if supported, false otherwise **/ BOOLEAN IsDimmInterfaceCodeSupported( IN DIMM *pDimm ); /** Check if the subsystem device ID of this DIMM is supported @param[in] pDimm the DIMM struct @retval true if supported, false otherwise **/ BOOLEAN IsSubsystemDeviceIdSupported( IN DIMM *pDimm ); /** Check if current firmware API version is supported @param[in] pDimm the DIMM struct @retval true if supported, false otherwise **/ BOOLEAN IsFwApiVersionSupported( IN DIMM *pDimm ); /** Clears the PCD Cache on each DIMM in the global DIMM list @retval EFI_SUCCESS Success **/ EFI_STATUS ClearPcdCacheOnDimmList(VOID); /** Return what passthru method will be used to send the command. @param[in] pDimm The DCPMM to transact with @param[in] Opcode of the FW command. Used to determine if command is dependent on DDRT/SMBUS interface status. @param[in] SubOpcode of the FW command. Used to determine if command is dependent on DDRT/SMBUS interface status. @param[in] IsLargePayloadCommand Need to know if large payload interface is even desired. If not, then it makes no sense to write to the large payload mailbox unless the user specifies it. @param[out] Method Pointer to passthru method variable to modify @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if we failed to do basic communication with the DCPMM **/ EFI_STATUS DeterminePassThruMethod( IN DIMM *pDimm, IN UINT8 Opcode, IN UINT8 SubOpcode, IN BOOLEAN IsLargePayloadCommand, OUT DIMM_PASSTHRU_METHOD *pMethod ); /** Set Obj Status when DIMM is not found using Id expected by end user @param[in] DimmId the Pid for the DIMM that was not found @param[in] pDimms Pointer to head of list where DimmId should be found @param[out] pCommandStatus Pointer to command status structure **/ VOID SetObjStatusForDimmNotFound( IN UINT16 DimmId, IN LIST_ENTRY *pDimms, OUT COMMAND_STATUS *pCommandStatus ); /** Set object status for DIMM @param[out] pCommandStatus Pointer to command status structure @param[in] pDimm DIMM for which the object status is being set @param[in] Status Object status to set @param[in] If TRUE - clear all other status before setting this one **/ VOID SetObjStatusForDimmWithErase( OUT COMMAND_STATUS *pCommandStatus, IN DIMM *pDimm, IN NVM_STATUS Status, IN BOOLEAN EraseFirst ); /** Determine the total size of PCD Config Data area by finding the largest offset any of the 3 data sets. @param[in] pOemHeader Pointer to NVDIMM Configuration Header @param[out] pOemDataSize Size of the PCD Config Data @retval EFI_INVALID_PARAMETER NULL pointer for DIMM structure provided @retval EFI_SUCCESS Success **/ EFI_STATUS GetPcdOemDataSize( NVDIMM_CONFIGURATION_HEADER *pOemHeader, UINT32 *pOemDataSize ); /** Check if sending a large payload command over the DDRT large payload mailbox is possible. Used by callers often to determine chunking behavior. @param[in] pDimm The DCPMM to transact with @param[out] pAvailable Whether large payload is available. Pointer to boolean variable @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR if we failed to do basic communication with the DCPMM **/ EFI_STATUS IsLargePayloadAvailable( IN DIMM *pDimm, OUT BOOLEAN *pAvailable ); EFI_STATUS PassThru( IN struct _DIMM *pDimm, IN OUT NVM_FW_CMD *pCmd, IN UINT64 Timeout ); /** Makes Bios emulated pass through call and acquires the DCPMM Boot Status Register @param[in] pDimm The DCPMM to retrieve identify info on @param[out] pBsrValue Pointer to memory to copy BSR value to @retval EFI_SUCCESS: Success @retval EFI_OUT_OF_RESOURCES: memory allocation failure **/ EFI_STATUS EFIAPI FwCmdGetBsr( IN DIMM *pDimm, OUT UINT64 *pBsrValue ); /** Populate interface bits in boot status bitmask based on PMem module DDRT(and optionally SMBUS) interface status @param[in] pDimm Pointer to PMem module @param[out] pBootStatusBitmask Pointer to boot status bitmask @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ EFI_STATUS PopulateDimmBootStatusBitmaskInterfaceBits( IN DIMM *pDimm, OUT UINT16 *pBootStatusBitmask ); /** Populate BSR bits in PMem module boot status bitmask based on boot status register value @param[in] pDimm Pointer to PMem module @param[in] pBsr Pointer to Boot Status Register(BSR) @param[out] pBootStatusBitmask Pointer to boot status bitmask @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ EFI_STATUS PopulateDimmBootStatusBitmaskBsrBits( IN DIMM *pDimm, IN DIMM_BSR *pBsr, OUT UINT16 *pBootStatusBitmask ); /** Passthrough FIS command by Dcpmm BIOS protocol. @param[in] pDimm Target DIMM structure pointer @param[in, out] pCmd Firmware command structure pointer @param[in] Timeout Optional command timeout in microseconds @param[in] DcpmmInterface Interface for FIS request @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS DcpmmCmd( IN struct _DIMM *pDimm, IN OUT NVM_FW_CMD *pCmd, IN UINT32 Timeout OPTIONAL, IN DCPMM_FIS_INTERFACE DcpmmInterface ); /** Get large payload info by Dcpmm BIOS protocol. @param[in] pDimm Target DIMM structure pointer @param[in] Timeout Optional command timeout in microseconds @param[in] DcpmmInterface Interface for FIS request @param[out] pOutput Large payload info output data buffer @param[out] pStatus FIS request status @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS DcpmmLargePayloadInfo( IN struct _DIMM *pDimm, IN UINT32 Timeout OPTIONAL, IN DCPMM_FIS_INTERFACE DcpmmInterface, OUT DCPMM_FIS_OUTPUT *pOutput, OUT UINT8 *pStatus ); /** Write large payload by Dcpmm BIOS protocol. @param[in] pDimm Target DIMM structure pointer @param[in] pInput Input data buffer @param[in] InputSize Total input data size @param[in] MaxChunkSize Maximum chunk of data to write @param[in] Timeout Optional command timeout in microseconds @param[in] DcpmmInterface Interface for FIS request @param[out] pStatus FIS request status @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS DcpmmLargePayloadWrite( IN struct _DIMM *pDimm, IN UINT8 *pInput, IN UINT32 InputSize, IN UINT32 MaxChunkSize, IN UINT32 Timeout OPTIONAL, IN DCPMM_FIS_INTERFACE DcpmmInterface, OUT UINT8 *pStatus ); /** Read large payload by Dcpmm BIOS protocol. @param[in] pDimm Target DIMM structure pointer @param[in] OutputSize Total output data size @param[in] MaxChunkSize Maximum chunk of data to read @param[in] Timeout Optional command timeout in microseconds @param[in] DcpmmInterface Interface for FIS request @param[out] pOutput Output data buffer @param[out] pStatus FIS request status pointer @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS DcpmmLargePayloadRead( IN struct _DIMM *pDimm, IN UINT32 OutputSize, IN UINT32 MaxChunkSize, IN UINT32 Timeout OPTIONAL, IN DCPMM_FIS_INTERFACE DcpmmInterface, IN OUT UINT8 *pOutput, OUT UINT8 *pStatus ); /** Helper function for setting appropriate command status and return code when checking for mixed SKU condition @param[out] pCommandStatus Pointer to command status variable @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pCommandStatus is NULL @retval EFI_UNSUPPORTED Mixed SKU condition occurred **/ EFI_STATUS BlockMixedSku( OUT COMMAND_STATUS* pCommandStatus ); #endif ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Namespace.c000066400000000000000000004772411440615110200214020ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include "Namespace.h" #ifndef OS_BUILD #include #endif #include #include "Region.h" #include "NvmSecurity.h" #include "NvmDimmBlockIo.h" #include "Btt.h" #include "Pfn.h" #include #include "AsmCommands.h" #include extern EFI_SYSTEM_TABLE *gSystemTable; extern EFI_DIMMS_DATA gDimmsUefiData[MAX_DIMMS]; extern NVMDIMMDRIVER_DATA *gNvmDimmData; extern EFI_BLOCK_IO_MEDIA gNvmDimmDriverBlockIoMedia; extern CONST UINT64 gSupportedBlockSizes[SUPPORTED_BLOCK_SIZES_COUNT]; #ifndef OS_BUILD extern DCPMM_ARS_ERROR_RECORD * gArsBadRecords; extern INT32 gArsBadRecordsCount; #endif extern VOID (*gClFlush)( VOID *pLinearAddress ); /** From EDK Network Controller Driver source code **/ #define GET_RANDOM_UINT32(Seed) ((UINT32) (((Seed) * 1103515245L + 12345) % 4294967295L)) /** Vendor specific namespaces device path For each namespace the UUID should be modified to distinct them. **/ VENDOR_DEVICE_PATH gVenHwNamespaceDevicePathNode = { { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { (UINT8)(sizeof(VENDOR_DEVICE_PATH)), (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8) } }, NVMDIMM_DRIVER_NGNVM_GUID //!< This GUID will be overwritten by the namespace UUID }; /** UEFI spec defined namespace device path. Per UEFI 2.8 For each namespace the UUID should be modified to distinct them. **/ NVDIMM_NAMESPACE_DEVICE_PATH gNvdimmNamespaceDevicePathNode = { { MESSAGING_DEVICE_PATH, NVDIMM_NAMESPACE_DP, { (UINT8)(sizeof(NVDIMM_NAMESPACE_DEVICE_PATH)), (UINT8)((sizeof(NVDIMM_NAMESPACE_DEVICE_PATH)) >> 8) } }, NVMDIMM_DRIVER_NGNVM_GUID //!< This GUID will be overwritten by the namespace UUID }; EFI_STATUS IsNamespaceLocked( IN NAMESPACE *pNamespace, OUT BOOLEAN *pIsLocked ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 SecurityState = 0; DIMM **ppDimms = NULL; UINT32 DimmsNum = 0; UINT32 Index = 0; NVDIMM_ENTRY(); if (pNamespace == NULL || pIsLocked == NULL) { goto Finish; } *pIsLocked = FALSE; if (pNamespace->pParentIS == NULL) { goto Finish; } DimmsNum = pNamespace->RangesCount; ppDimms = AllocateZeroPool(sizeof(*ppDimms) * DimmsNum); if (ppDimms == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } Index = 0; for (Index = 0; Index < DimmsNum; Index++) { ppDimms[Index] = pNamespace->Range[Index].pDimm; } for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = GetDimmSecurityState(ppDimms[Index], PT_TIMEOUT_INTERVAL, &SecurityState); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to get DIMM security state."); goto Finish; } if (!IsConfiguringAllowed(SecurityState)) { NVDIMM_DBG("Locked namespace discovered %g",pNamespace->NamespaceGuid); *pIsLocked = TRUE; goto Finish; } } Finish: FREE_POOL_SAFE(ppDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** RegisterNamespaceName Adds the Namespace name for the use of the ComponentName protocols. @param[in] pNamespace, the pointer of the Namespace structure that the caller wants to register the name for. @retval EFI_SUCCESS if the name was added successfully Other return values from the function AddStringToUnicodeTable. **/ STATIC EFI_STATUS RegisterNamespaceName( IN NAMESPACE *pNamespace ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 *pNamespaceName = NULL; EFI_UNICODE_STRING_TABLE *pNamespaceNameTable = NULL; NVDIMM_ENTRY(); pNamespaceName = CatSPrint(NULL, (PRODUCT_NAME L" Namespace Id %d"), pNamespace->NamespaceId); if (pNamespaceName != NULL) { ReturnCode = AddStringToUnicodeTable(pNamespaceName, &pNamespaceNameTable); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to create the unicode namespace name.\n"); goto Finish; } pNamespace->pNamespaceName = pNamespaceNameTable; } else { /** This is not a critical error - the driver will still work properly. We report a warning. **/ NVDIMM_DBG("Failed to allocate memory for Namespace name.\n"); } Finish: FREE_POOL_SAFE(pNamespaceName); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** This function tries to match the given EFI_HANDLE in the list of existing block namespace handles. @param[in] Handle - the EFI_HANDLE that the caller needs the NAMESPACE structure pointer to. @retval NULL - if the handle did not match any installed handles. @retval pointer to the result NAMESPACE structure. **/ NAMESPACE * HandleToNamespace( IN EFI_HANDLE Handle ) { LIST_ENTRY *pNamespaceNode = NULL; NAMESPACE *pNamespace = NULL; for (pNamespaceNode = GetFirstNode(&gNvmDimmData->PMEMDev.Namespaces); !IsNull(&gNvmDimmData->PMEMDev.Namespaces, pNamespaceNode); pNamespaceNode = GetNextNode(&gNvmDimmData->PMEMDev.Namespaces, pNamespaceNode)) { pNamespace = NAMESPACE_FROM_NODE(pNamespaceNode, NamespaceNode); if (pNamespace->BlockIoHandle == Handle) { return pNamespace; } } return NULL; } /** Iterates over all of the existing namespaces and installs block io protocols This function will install two protocols: block and device path on the namespace handle. Also it will allocate memory for the block device (child) device path instance so it can be attached to the Controller (provided by the parameter). At last, the function will open the Controllers device path instance as a child device. Before unloading the driver, CleanNamespaces should be run to close, uninstall those protocols and to free the memory. @retval EFI_SUCCESS the iteration and installation were successful. Other errors from InstallMultipleProtocolInterfaces and OpenProtocol functions. Warning! This function does not exit at the first error. It will be logged and then it continues to next namespace, trying to create as much working block devices as it is possible. **/ EFI_STATUS EFIAPI InstallProtocolsOnNamespaces( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LIST_ENTRY *pNamespaceNode = NULL; NAMESPACE *pNamespace = NULL; NVDIMM_ENTRY(); for (pNamespaceNode = GetFirstNode(&gNvmDimmData->PMEMDev.Namespaces); !IsNull(&gNvmDimmData->PMEMDev.Namespaces, pNamespaceNode); pNamespaceNode = GetNextNode(&gNvmDimmData->PMEMDev.Namespaces, pNamespaceNode)) { pNamespace = NAMESPACE_FROM_NODE(pNamespaceNode, NamespaceNode); // We do not install the protocol on disabled block namespaces if (!pNamespace->Enabled) { continue; } ReturnCode = InstallNamespaceProtocols(pNamespace); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_NOT_READY || ReturnCode == EFI_ACCESS_DENIED) { NVDIMM_WARN("Namespace not enabled or invalid security state! Skipping the protocols installation."); ReturnCode = EFI_SUCCESS; } } // Reconnect the protocols that are built on top of the BlockIo protocol // (DiskIo, SimpleFileSystem). This is somehow not required during normal driver load // but is necessary during ReenumerateNamespacesAndISs(TRUE) where everything is reloaded. CHECK_RESULT_CONTINUE(gBS->ConnectController(pNamespace->BlockIoHandle, NULL, NULL, TRUE)); } return ReturnCode; } /** This function closes all protocols opened by the block devices handles, it will also uninstall the block and device path protocols. At the end it deallocated the memory taken for the device path protocol instance. @retval EFI_SUCCESS the cleanup completed successfully. Other errors returned from UninstallMultipleProtocolInterfaces function. Warning! In case of an error, the cleanup does not break, it tries to clean the rest of the DIMMs. **/ EFI_STATUS EFIAPI CleanNamespaces( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LIST_ENTRY *pNamespaceNode = NULL; NAMESPACE *pNamespace = NULL; NVDIMM_ENTRY(); for (pNamespaceNode = GetFirstNode(&gNvmDimmData->PMEMDev.Namespaces); !IsNull(&gNvmDimmData->PMEMDev.Namespaces, pNamespaceNode); pNamespaceNode = GetNextNode(&gNvmDimmData->PMEMDev.Namespaces, pNamespaceNode)) { pNamespace = NAMESPACE_FROM_NODE(pNamespaceNode, NamespaceNode); ReturnCode = UninstallNamespaceProtocols(pNamespace); if (pNamespace->IsBttEnabled && pNamespace->pBtt != NULL) { BttRelease(pNamespace->pBtt); pNamespace->pBtt = NULL; } } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Attaches the block device to the DIMM and installs the Block IO Protocol to the Namespace. @param[in,out] pNamespace - is the pointer to the NAMESPACE structure that is supposed to be exposed in the system. @retval EFI_INVALID_PARAMETER if the pNamespace equals NULL. @retval EFI_ABORTED if the parent DIMM block window was not properly initialized or the DIMM security state could not be determined. @retval EFI_NOT_READY if the Namespace was not Enabled. @retval EFI_ACCESS_DENIED if the DIMM is locked @retval EFI_SUCCESS if the operation completed successfully. Or the Namespace is not Block. Other return values from OpenProtocol and InstallMultipleProtocolInterfaces function. **/ EFI_STATUS InstallNamespaceProtocols( IN OUT NAMESPACE *pNamespace ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_DEVICE_PATH_PROTOCOL *pTempDevicePathInterface = NULL; EFI_DEVICE_PATH_PROTOCOL *pParentDevicePath = NULL; UINT32 MediaBlockSize = 0; VENDOR_DEVICE_PATH *pVenHwNamespaceDevicePath = NULL; NVDIMM_NAMESPACE_DEVICE_PATH *pNvdimmNamespaceDevicePath = NULL; NVDIMM_ENTRY(); if (pNamespace == NULL) { goto Finish; } if (pNamespace->ProtocolsInstalled) { /** Already installed **/ ReturnCode = EFI_SUCCESS; goto Finish; } if (!pNamespace->Enabled) { ReturnCode = EFI_NOT_READY; goto Finish; } if (pNamespace->pParentIS == NULL) { goto Finish; } pParentDevicePath = gNvmDimmData->pControllerDevicePathInstance; // Use standardized path if namespace is bootable if ((pNamespace->IsBttEnabled == TRUE ) || (pNamespace->IsPfnEnabled == TRUE) || (pNamespace->IsRawNamespace == TRUE)) { NVDIMM_DBG("Using NVDIMM Namespace Device Path"); pNvdimmNamespaceDevicePath = AllocateZeroPool(sizeof(NVDIMM_NAMESPACE_DEVICE_PATH)); if (NULL == pNvdimmNamespaceDevicePath) { NVDIMM_DBG("Failed to initialize the pNvdimmNamespaceDevicePath.\n"); ReturnCode = EFI_ABORTED; goto Finish; } pNvdimmNamespaceDevicePath->Header.Type = MESSAGING_DEVICE_PATH; pNvdimmNamespaceDevicePath->Header.SubType = NVDIMM_NAMESPACE_DP; pNvdimmNamespaceDevicePath->Header.Length[0] = (UINT8)(sizeof(NVDIMM_NAMESPACE_DEVICE_PATH)); pNvdimmNamespaceDevicePath->Header.Length[1] = (UINT8)((sizeof(NVDIMM_NAMESPACE_DEVICE_PATH)) >> 8); CopyMem_S(&pNvdimmNamespaceDevicePath->Uuid, sizeof(pNvdimmNamespaceDevicePath->Uuid), pNamespace->NamespaceGuid, sizeof(pNvdimmNamespaceDevicePath->Uuid)); pNamespace->pBlockDevicePath = AppendDevicePathNode( NULL, (CONST EFI_DEVICE_PATH_PROTOCOL *) pNvdimmNamespaceDevicePath); } else { NVDIMM_DBG("Using VenHw Namespace Device Path"); pVenHwNamespaceDevicePath = AllocateZeroPool(sizeof(VENDOR_DEVICE_PATH)); if (NULL == pVenHwNamespaceDevicePath) { NVDIMM_DBG("Failed to initialize the pVenHwNamespaceDevicePath.\n"); ReturnCode = EFI_ABORTED; goto Finish; } pVenHwNamespaceDevicePath->Header.Type = HARDWARE_DEVICE_PATH; pVenHwNamespaceDevicePath->Header.SubType = HW_VENDOR_DP; pVenHwNamespaceDevicePath->Header.Length[0] = (UINT8)(sizeof(VENDOR_DEVICE_PATH)); pVenHwNamespaceDevicePath->Header.Length[1] = (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8); CopyMem_S(&pVenHwNamespaceDevicePath->Guid, sizeof(pVenHwNamespaceDevicePath->Guid), pNamespace->NamespaceGuid, sizeof(pVenHwNamespaceDevicePath->Guid)); pNamespace->pBlockDevicePath = AppendDevicePathNode( pParentDevicePath, (CONST EFI_DEVICE_PATH_PROTOCOL *) pVenHwNamespaceDevicePath); } pNamespace->BlockIoInstance = gNvmDimmDriverBlockIo; // Get the logical block size before the Block IO Media copy MediaBlockSize = (UINT32)GetBlockDeviceBlockSize(pNamespace); if (MediaBlockSize == 0) { // Don't install the protocol if the size is 0. ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } CopyMem_S(&pNamespace->Media, sizeof(pNamespace->Media), &gNvmDimmDriverBlockIoMedia, sizeof(pNamespace->Media)); // Overwrite with actual namespace size pNamespace->Media.BlockSize = MediaBlockSize; pNamespace->Media.OptimalTransferLengthGranularity = pNamespace->Media.BlockSize; pNamespace->Media.LastBlock = GetAccessibleCapacity(pNamespace) / pNamespace->Media.BlockSize - 1; if (pNamespace->IsBttEnabled) { if (pNamespace->pBtt == NULL) { NVDIMM_DBG("Failed to initialize the BTT.\n"); ReturnCode = EFI_ABORTED; goto Finish; } // In case of Btt enabled we need to calculate LastBlock based on BTT LBA count pNamespace->Media.LastBlock = pNamespace->pBtt->NLbas - 1; } pNamespace->BlockIoInstance.Media = &pNamespace->Media; ReturnCode = RegisterNamespaceName(pNamespace); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Could not initialize the namespace name."); } ReturnCode = gBS->InstallProtocolInterface( &pNamespace->BlockIoHandle, &gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE, pNamespace->pBlockDevicePath); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to install the device path protocol, error = " FORMAT_EFI_STATUS "\n.", ReturnCode); } ReturnCode = gBS->InstallMultipleProtocolInterfaces( &pNamespace->BlockIoHandle, &gEfiBlockIoProtocolGuid, &pNamespace->BlockIoInstance, // We might also need the BlockIo2Protocol while we are not sure, lets leave this as commented. //&gEfiBlockIo2ProtocolGuid, &gTestDeviceBlockIo2Protocol, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to install the block io protocol, error = " FORMAT_EFI_STATUS "\n.", ReturnCode); } else { ReturnCode = gBS->OpenProtocol( gNvmDimmData->ControllerHandle, &gEfiDevicePathProtocolGuid, (VOID **)&pTempDevicePathInterface, gNvmDimmData->DriverHandle, pNamespace->BlockIoHandle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to initialize the Block Device, error = " FORMAT_EFI_STATUS "\n.", ReturnCode); } } pNamespace->ProtocolsInstalled = TRUE; Finish: FREE_POOL_SAFE(pNvdimmNamespaceDevicePath); FREE_POOL_SAFE(pVenHwNamespaceDevicePath); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Detaches the block device from the DIMM and uninstalls the Block IO Protocol from the Namespace. If the Namespace is not enabled, no actions are taken. @param[in,out] pNamespace - is the pointer to the NAMESPACE structure that is supposed to be removed from the system. @retval EFI_INVALID_PARAMETER if the pNamespace equals NULL. @retval EFI_ABORTED if the Block IO instance handle equals NULL and no actions can be performed. @retval EFI_SUCCESS if the operation completed successfully. Or the Namespace is not Block. Other return values from UninstallMultipleProtocolInterfaces function. **/ EFI_STATUS UninstallNamespaceProtocols( IN OUT NAMESPACE *pNamespace ) { EFI_STATUS ReturnCode = EFI_ABORTED; EFI_DEVICE_PATH *pBlockDevicePath = NULL; UINT16 DimmDataIndex = DIMM_PID_INVALID; EFI_HANDLE DimmHandle = NULL; NVDIMM_ENTRY(); if (pNamespace == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (!pNamespace->Enabled) { ReturnCode = EFI_SUCCESS; goto Finish; } pBlockDevicePath = pNamespace->pBlockDevicePath; if (pNamespace->BlockIoHandle == NULL) { NVDIMM_DBG("The DIMM Block IO handle is NULL, nothing to uninstall."); ReturnCode = EFI_SUCCESS; goto Finish; } DimmDataIndex = GetDimmEfiDataIndex(0, pNamespace->pParentDimm, NULL); if (DimmDataIndex != DIMM_PID_INVALID) { DimmHandle = gDimmsUefiData[DimmDataIndex].DeviceHandle; NVDIMM_DBG("DimmDataIndex -- gDimmsUefiData[%d].DeviceHandle = %d", DimmDataIndex, DimmHandle); } else { NVDIMM_DBG("DimmDataIndex (%d) = DIMM_PID_INVALID... DimmHandle was not set", DimmDataIndex); } NVDIMM_DBG("DimmHandle = %d", DimmHandle); NVDIMM_DBG("gNvmDimmData->ControllerHandle = %d", gNvmDimmData->ControllerHandle); NVDIMM_DBG("gNvmDimmData->DriverHandle = %d", gNvmDimmData->DriverHandle); NVDIMM_DBG("pNamespace->BlockIoHandle = %d", pNamespace->BlockIoHandle); ReturnCode = gBS->CloseProtocol( (pNamespace->Flags.Values.Local) ? DimmHandle : gNvmDimmData->ControllerHandle, &gEfiDevicePathProtocolGuid, gNvmDimmData->DriverHandle, pNamespace->BlockIoHandle ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to detach the block device from parent device."); NVDIMM_WARN("Error = " FORMAT_EFI_STATUS "\n.", ReturnCode); if (pNamespace->Flags.Values.Local) { NVDIMM_DBG("CloseProtocol failed using DimmHandle."); NVDIMM_DBG("Attempting CloseProtocol with ControllerHandle"); ReturnCode = gBS->CloseProtocol( gNvmDimmData->ControllerHandle, &gEfiDevicePathProtocolGuid, gNvmDimmData->DriverHandle, pNamespace->BlockIoHandle ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to detach the block device from parent device."); NVDIMM_WARN("Error = " FORMAT_EFI_STATUS "\n.", ReturnCode); goto Finish; } } else { NVDIMM_DBG("CloseProtocol failed using ControllerHandle."); NVDIMM_DBG("Attempting CloseProtocol with DimmHandle"); ReturnCode = gBS->CloseProtocol( DimmHandle, &gEfiDevicePathProtocolGuid, gNvmDimmData->DriverHandle, pNamespace->BlockIoHandle ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to detach the block device from parent device."); NVDIMM_WARN("Error = " FORMAT_EFI_STATUS "\n.", ReturnCode); goto Finish; } } } ReturnCode = gBS->UninstallMultipleProtocolInterfaces( pNamespace->BlockIoHandle, &gEfiDevicePathProtocolGuid, pNamespace->pBlockDevicePath, &gEfiBlockIoProtocolGuid, &pNamespace->BlockIoInstance, // We might also need the BlockIo2Protocol while we are not sure, lets leave this as commented. //&gEfiBlockIo2ProtocolGuid, &pNamespace->BlockIoInstance, NULL ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to uninstall the block device protocols. Error = " FORMAT_EFI_STATUS "\n.", ReturnCode); NVDIMM_WARN("The device may be still visible in the system and accessing it may cause unpredicted behavior."); } else { // Free the instance only if the protocol was uninstalled successfully. FREE_POOL_SAFE(pBlockDevicePath); pNamespace->pBlockDevicePath = NULL; pNamespace->BlockIoHandle = NULL; ReturnCode = FreeUnicodeStringTable(pNamespace->pNamespaceName); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to free the namespace unicode name. Error = " FORMAT_EFI_STATUS "\n.", ReturnCode); } else { // The memory is cleared in the FreeUnicodeStringTable function pNamespace->pNamespaceName = NULL; } } pNamespace->ProtocolsInstalled = FALSE; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** GetNamespace Looks through the list of namespaces, searching for a namespace with a particular GUID. @param[in] pNamespacesList pointer to the list where the function is supposed to search for the Namespace. @param[in] Uuid the target UUID of the Namespace that the function is searching for. @param[out] ppNamespace pointer to a pointer of where the Namespace pointer will be stored. @retval FALSE At least one of the input pointers is NULL or there is no Namespace with the specified UUID on the list. @retval TRUE the Namespace has been found and is stored under the ppNamespace. **/ STATIC BOOLEAN GetNamespace( IN LIST_ENTRY *pNamespacesList, IN GUID Uuid, OUT NAMESPACE **ppNamespace ); /** Initialize a random seed using current time. Get current time first. Then initialize a random seed based on some basic mathematics operation on the hour, day, minute, second, nanosecond and year of the current time. @return The random seed initialized with current time. @return 0 if there was an error while getting the current time. **/ UINT32 EFIAPI GenerateCurrentTimeSeed( VOID ) { EFI_TIME Time; UINT32 Seed = 0; SetMem(&Time, sizeof(Time), 0x0); if (gSystemTable->RuntimeServices != NULL && !EFI_ERROR(gSystemTable->RuntimeServices->GetTime(&Time, NULL))) { Seed = (~Time.Hour << 24 | Time.Day << 16 | Time.Minute << 8 | Time.Second); Seed ^= Time.Nanosecond; Seed ^= Time.Year << 7; } return Seed; } /** Generate random numbers in a buffer. @param[in, out] Rand The buffer to contain random numbers. @param[in] RandLength The length of the Rand buffer. **/ VOID RandomizeBuffer( IN OUT UINT8 *Rand, IN UINT64 RandLength ) { STATIC UINT32 Next = 0; if (Next == 0) { Next = GenerateCurrentTimeSeed(); } while (RandLength > 0) { Next = GET_RANDOM_UINT32(Next); *Rand = (UINT8) Next; Rand++; RandLength--; } } /** Generate a NamespaceId value Namespace Id is a 16bit value and consists of the InterleaveSetIndex/RegionId (upper 8bits) and slot index (lower 8 bits). Neither InterleaveSetIndex nor slot index can equal zero. The lowest namespace Id value is 0x0101. @retval The generated ID **/ UINT16 EFIAPI GenerateNamespaceId(UINT16 RequestedRegionId) { NAMESPACE *pNamespace = NULL; LIST_ENTRY *pNode = NULL; UINT16 NamespaceId = CREATE_NAMESPACE_ID(RequestedRegionId, 0); LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Namespaces) { pNamespace = NAMESPACE_FROM_NODE(pNode, NamespaceNode); if (pNamespace->pParentIS->RegionId != RequestedRegionId) { continue; // Find the namespace with requested Region ID } if (pNamespace->NamespaceId == NamespaceId + 1) { NamespaceId = pNamespace->NamespaceId; } else if (pNamespace->NamespaceId > NamespaceId) { break; } } NamespaceId++; return NamespaceId; } /** Generate a random (version 4) GUID @retval The generated GUID @retval Zeroed buffer, if there was a problem while getting the time seed **/ VOID EFIAPI GenerateRandomGuid( OUT GUID *pResultGuid ) { #ifndef OS_BUILD GUID GeneratedGuid; SetMem(&GeneratedGuid, sizeof(GeneratedGuid), 0x0); GetRandomNumber128((UINT64*)&GeneratedGuid); // Set the version of the GUID to the 4th version (Random GUID) GeneratedGuid.Data3 |= 0x4000; // 0b0100000000000000 Make sure that the 4 bits are set GeneratedGuid.Data3 &= 0x4FFF; // 0b0100111111111111 Zero other bits from the highest hex // Set two highest bits from Data4 to 10 (accordingly to the spec) GeneratedGuid.Data4[0] |= 0x80; // 0b10000000 Set the highest bit GeneratedGuid.Data4[0] &= 0xBF; // 0b10111111 Clear the 7th bit CopyMem_S(pResultGuid, sizeof(*pResultGuid), &GeneratedGuid, sizeof(*pResultGuid)); #endif } /** Function changes Namespace slot status to a required state. Slot status can be free or occupied. Appropriate bit is set or cleared in specified index block free bitmap. @param[in] pIndex Index Block in which to update free status @param[in] SlotNumber Number of a slot on which to update status @param[in] NewStatus Predefined value representing new status. This can be SLOT_FREE or SLOT_USED. @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_SUCCESS Operation successful **/ EFI_STATUS ChangeSlotStatus( IN NAMESPACE_INDEX *pIndex, IN UINT16 SlotNumber, IN UINT16 NewStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CONST UINT16 BitsInBlock = sizeof(UINT8) * 8; // how many bits in a block CONST UINT16 BlockNumber = SlotNumber / BitsInBlock; // subsequent block number in the bitmap CONST UINT8 BitNumber = (CONST UINT8)(SlotNumber % BitsInBlock); // subsequent bit number in a block NVDIMM_ENTRY(); if (pIndex == NULL) { goto Finish; } if (NewStatus == SLOT_FREE) { pIndex->pFree[BlockNumber] |= (1 << BitNumber); // free slot marked with set bit } else if (NewStatus == SLOT_USED) { pIndex->pFree[BlockNumber] &= ~(1 << BitNumber); // used slot marked with cleared bit } else { NVDIMM_DBG("Invalid slot status provided"); goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get Namespace label from of specified DIMM by its UUID. Function allocates memory for the label data. It is callee responsibility to free it after it is no longer needed. If more than one label is detected then return error. @param[in] pDimm Target DIMM @param[in] UUID Namespace UUID @param[out] ppNamespaceLabel Pointer to a location where store the label @retval EFI_INVALID_PARAMETER NULL pointer parameter provided @retval EFI_OUT_OF_RESOURCES No memory to allocate label structure @retval EFI_DEVICE_ERROR More than one label found @retval EFI_SUCCESS Current Index position found **/ EFI_STATUS GetAppDirectLabelByUUID( IN DIMM *pDimm, IN GUID UUID, OUT NAMESPACE_LABEL **ppNamespaceLabel ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LABEL_STORAGE_AREA *pLsa = NULL; NAMESPACE_LABEL *pLabel = NULL; UINT16 CurrentIndex = 0; UINT16 Index = 0; BOOLEAN Found = FALSE; UINT16 SlotStatus = SLOT_UNKNOWN; BOOLEAN Use_Namespace1_1 = FALSE; BOOLEAN lsaIsLocal = FALSE; NVDIMM_ENTRY(); if (pDimm == NULL || ppNamespaceLabel == NULL) { goto Finish; } if (pDimm->pLsa == NULL) { NVDIMM_DBG("Loading the actual cache"); lsaIsLocal = TRUE; ReturnCode = ReadLabelStorageArea(pDimm->DimmID, &pLsa); if (EFI_ERROR(ReturnCode) || pLsa == NULL) { goto Finish; } } else { NVDIMM_DBG("Using LSA cache pointer on DIMM"); pLsa = pDimm->pLsa; } ReturnCode = GetLsaIndexes(pLsa, &CurrentIndex, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = EFI_NOT_FOUND; for (Index = 0; Index < pLsa->Index[CurrentIndex].NumberOfLabels; Index++) { CheckSlotStatus(&pLsa->Index[CurrentIndex], Index, &SlotStatus); if (SlotStatus == SLOT_FREE) { continue; } if (pLsa->Index[CurrentIndex].Major == NSINDEX_MAJOR && pLsa->Index[CurrentIndex].Minor == NSINDEX_MINOR_1) { Use_Namespace1_1 = TRUE; } if (!IsNameSpaceTypeAppDirect(&pLsa->pLabels[Index], Use_Namespace1_1)) { continue; } if (CompareMem(&UUID, &pLsa->pLabels[Index].Uuid, sizeof(UUID)) != 0) { continue; } if (Found == TRUE) { NVDIMM_DBG("More than one AppDirect label with the same UUID found (DIMM=%d, pos=%d)", pDimm->DimmID, Index); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } pLabel = AllocateZeroPool(sizeof(*pLabel)); if (pLabel == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } CopyMem_S(pLabel, sizeof(*pLabel), &pLsa->pLabels[Index], sizeof(*pLabel)); *ppNamespaceLabel = pLabel; Found = TRUE; } /** Return EFI_SUCCESS if a label was found **/ if (Found == TRUE) { ReturnCode = EFI_SUCCESS; } Finish: if (lsaIsLocal == TRUE && pLsa != NULL) { FreeLsaSafe(&pLsa); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Sanity checks of a Namespace Label @param[in] pNamespaceLabel Pointer to a Namespace label struct to be checked @param[in] pLabelStorageArea Pointer to a LSA to which NamespaceLabel belongs @retval EFI_INVALID_PARAMETER Null pointer provided @retval EFI_VOLUME_CORRUPTED The label is not valid @retval EFI_SUCCESS The label is valid **/ EFI_STATUS ValidateNamespaceLabel( IN NAMESPACE_LABEL *pNamespaceLabel, IN BOOLEAN Use_Namespace1_1 ) { EFI_STATUS ReturnCode = EFI_VOLUME_CORRUPTED; NVDIMM_ENTRY(); if (pNamespaceLabel == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Common sanity checks if (pNamespaceLabel->RawSize == 0) { goto Finish; } // Block namespace checks if (!IsNameSpaceTypeAppDirect(pNamespaceLabel, Use_Namespace1_1) && (pNamespaceLabel->LbaSize == 0 || pNamespaceLabel->Position != 0 || pNamespaceLabel->NumberOfLabels != 0)) { goto Finish; } // AppDirect namespace checks if (IsNameSpaceTypeAppDirect(pNamespaceLabel, Use_Namespace1_1) && (pNamespaceLabel->Position > pNamespaceLabel->NumberOfLabels)) { goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Checks provided Label Storage Area for consistency. Function runs a number of checks to determine whether LSA contains valid data. @param[in] pLabelStorageArea Pointer to a LSA structure @retval EFI_INVALID_PARAMETER Provided structure is NULL or contains errors @retval EFI_OUT_OF_RESOURCES If a memory allocation operation failed. @retval EFI_VOLUME_CORRUPTED There are no valid indexes in the LSA buffer @retval EFI_NOT_FOUND The LSA buffer is empty (filled with zeros) @retval EFI_SUCCESS Provided structure contains valid data **/ EFI_STATUS ValidateLsaData( IN LABEL_STORAGE_AREA *pLabelStorageArea ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT128 NamespaceSignature; BOOLEAN IndexesValid[2]; BOOLEAN ChecksumMatched = FALSE; UINT32 Index = 0; LABEL_STORAGE_AREA *pEmptyLsa = NULL; UINT8 *pRawData = NULL; UINT64 IndexLength = 0; UINT32 ChecksumOffset = 0; NVDIMM_ENTRY(); SetMem(&NamespaceSignature, sizeof(NamespaceSignature), 0x0); SetMem(IndexesValid, sizeof(IndexesValid), TRUE); if (pLabelStorageArea == NULL) { goto Finish; } pEmptyLsa = AllocateZeroPool(sizeof(*pEmptyLsa)); if (pEmptyLsa == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } if (CompareMem(pLabelStorageArea, pEmptyLsa, sizeof(*pLabelStorageArea)) == 0) { ReturnCode = EFI_NOT_FOUND; goto Finish; } ChecksumOffset = OFFSET_OF(NAMESPACE_INDEX, Checksum); /** Index Blocks tests **/ for (Index = 0; Index < NAMESPACE_INDEXES; ++Index) { NVDIMM_DBG("Signature of the NAMESPACE INDEX %d", Index); // We are copying the signature in two steps so we need to copy twice the half of the NSINDEX_SIG_LEN length. CopyMem_S(&NamespaceSignature.Uint64, sizeof(NamespaceSignature.Uint64), &pLabelStorageArea->Index[Index].Signature, NSINDEX_SIG_LEN / 2); CopyMem_S(&NamespaceSignature.Uint64_1, sizeof(NamespaceSignature.Uint64_1), &pLabelStorageArea->Index[Index].Signature[NSINDEX_SIG_LEN / 2], NSINDEX_SIG_LEN / 2); NVDIMM_HEXDUMP(&NamespaceSignature, sizeof(NamespaceSignature)); if (NamespaceSignature.Uint64 != LSA_NAMESPACE_INDEX_SIG_L || NamespaceSignature.Uint64_1 != LSA_NAMESPACE_INDEX_SIG_H) { NVDIMM_DBG("The signature of the NAMESPACE INDEX %d is incorrect.", Index); IndexesValid[Index] = FALSE; continue; } ReturnCode = LabelIndexAreaToRawData(pLabelStorageArea, Index, &pRawData); if (EFI_ERROR(ReturnCode) || (pRawData == NULL)) { NVDIMM_DBG("Failed to convert label area index to raw data"); FREE_POOL_SAFE(pRawData); goto Finish; } ReturnCode = EFI_INVALID_PARAMETER; IndexLength = pLabelStorageArea->Index[Index].MySize; ChecksumMatched = ChecksumOperations(pRawData, IndexLength, (UINT64 *)(pRawData + ChecksumOffset), FALSE); FREE_POOL_SAFE(pRawData); if (!ChecksumMatched) { NVDIMM_DBG("Incorrect checksum of the NAMESPACE INDEX %d.", Index); #ifndef WA_SKIP_LSA_CHECKSUM_FAIL IndexesValid[Index] = FALSE; continue; #endif } if (pLabelStorageArea->Index[Index].MyOffset != (Index * IndexLength) || pLabelStorageArea->Index[Index].OtherOffset != (Index == 0 ? IndexLength : 0)) { NVDIMM_DBG("Size and/or offsets are incorrect in NAMESPACE INDEX %d.", Index); NVDIMM_DBG("MyOffset: %d, MySize: %d, OtherOffset: %d", pLabelStorageArea->Index[Index].MyOffset, pLabelStorageArea->Index[Index].MySize, pLabelStorageArea->Index[Index].OtherOffset); IndexesValid[Index] = FALSE; continue; } if (pLabelStorageArea->Index[Index].Sequence == 0 || pLabelStorageArea->Index[Index].Sequence > 3) { NVDIMM_DBG("Invalid sequence number in the NAMESPACE INDEX %d.", Index); IndexesValid[Index] = FALSE; continue; } if (pLabelStorageArea->Index[Index].Major != NSINDEX_MAJOR) { NVDIMM_DBG("Index Major Version %d not supported NAMESPACE INDEX %d.", pLabelStorageArea->Index[Index].Major, Index); IndexesValid[Index] = FALSE; continue; } if (pLabelStorageArea->Index[Index].Minor != NSINDEX_MINOR_1 && pLabelStorageArea->Index[Index].Minor != NSINDEX_MINOR_2) { NVDIMM_DBG("Index Minor Version %d not supported NAMESPACE INDEX %d.", pLabelStorageArea->Index[Index].Minor, Index); IndexesValid[Index] = FALSE; continue; } if (pLabelStorageArea->Index[Index].Minor >= NSINDEX_MINOR_2 && pLabelStorageArea->Index[Index].LabelSize == 0) { NVDIMM_DBG("Invalid label size, Index %d: %d", Index, INDEX_LABEL_SIZE_TO_BYTE(pLabelStorageArea->Index[Index].LabelSize)); IndexesValid[Index] = FALSE; continue; } if (pLabelStorageArea->Index[Index].Minor >= NSINDEX_MINOR_2 && pLabelStorageArea->Index[Index].LabelSize != BYTE_TO_INDEX_LABEL_SIZE(sizeof(*(pLabelStorageArea->pLabels)))) { NVDIMM_DBG("Invalid label size, Index %d: %d", Index, INDEX_LABEL_SIZE_TO_BYTE(pLabelStorageArea->Index[Index].LabelSize)); IndexesValid[Index] = FALSE; continue; } } if (!IndexesValid[FIRST_INDEX_BLOCK] && !IndexesValid[SECOND_INDEX_BLOCK]) { NVDIMM_DBG("No valid Index Blocks found."); ReturnCode = EFI_VOLUME_CORRUPTED; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pEmptyLsa); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Copies the Raw LSA Index data into proper data structures It is the caller's responsibility to free the memory. @param[in] pRawData Raw data from the PCD @param[in] PcdLsaPartitionSize for checking Index size @param[out] pLsa The Label Storage Area structure @retval EFI_INVALID_PARAMETER Invalid parameter passed @retval EFI_SUCCESS Data was copied successfully **/ EFI_STATUS RawDataToLabelIndexArea( IN UINT8 *pRawData, IN UINT32 PcdLsaPartitionSize, OUT LABEL_STORAGE_AREA *pLsa ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NAMESPACE_INDEX *pNamespaceIndex = NULL; NAMESPACE_INDEX *pRawNamespaceIndex = NULL; UINT8 *pBuffer; UINT32 Index = 0; UINT64 FreeOffset = 0; UINT64 NumFreeBytes = 0; UINT64 IndexSize = 0; UINT64 CalculatedIndexSize = 0; UINT64 LabelSize = 0; UINT64 TotalSize = 0; if ((pRawData == NULL) || pLsa == NULL) { goto Finish; } pBuffer = pRawData; pRawNamespaceIndex = (NAMESPACE_INDEX *)pRawData; IndexSize = pRawNamespaceIndex->MySize; if (IndexSize == 0) { // Returning success as it could mean there is no LSA initialized // and that is fine, as this function is called while reading LSA ReturnCode = EFI_SUCCESS; goto Finish; } LabelSize = INDEX_LABEL_SIZE_TO_BYTE(pRawNamespaceIndex->LabelSize); // Total label space we can use is size of 2 Index blocks + size of max number of labels TotalSize = (IndexSize * 2) + (LabelSize * pRawNamespaceIndex->NumberOfLabels); FreeOffset = OFFSET_OF(NAMESPACE_INDEX, pFree); NumFreeBytes = LABELS_TO_FREE_BYTES(ROUNDUP(pRawNamespaceIndex->NumberOfLabels, NSINDEX_FREE_ALIGN)); CalculatedIndexSize = ROUNDUP(FreeOffset + NumFreeBytes, NSINDEX_ALIGN); // Sanity checks // Check size in the label matches static struct size + free + align // Check if Index + labels based on passed in NSlot would actually fit in LSA if (IndexSize != CalculatedIndexSize) { NVDIMM_WARN("Invalid index size. Label size: %d Calculated size: %d", IndexSize, CalculatedIndexSize); ReturnCode = EFI_VOLUME_CORRUPTED; goto Finish; } else if (TotalSize > PcdLsaPartitionSize) { NVDIMM_WARN("Invalid index size or NSlot. Label size: %d NSlot: %d", IndexSize, pRawNamespaceIndex->NumberOfLabels); ReturnCode = EFI_VOLUME_CORRUPTED; goto Finish; } for (Index = 0; Index < NAMESPACE_INDEXES; Index++) { pNamespaceIndex = &pLsa->Index[Index]; CopyMem_S(pNamespaceIndex, sizeof(*pNamespaceIndex), pBuffer, FreeOffset); pNamespaceIndex->pFree = AllocateZeroPool(NumFreeBytes); if (pNamespaceIndex->pFree == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } CopyMem_S(pNamespaceIndex->pFree, NumFreeBytes, pBuffer + FreeOffset, NumFreeBytes); pBuffer += IndexSize; } ReturnCode = EFI_SUCCESS; Finish: return ReturnCode; } /** Reads Label Storage Area of a specified DIMM. Function reads Platform Config Data partition 3. of a DIMM and invokes validation subroutine to check for data consistency. Required memory will be allocated, it is caller responsibility to free it after it is no longer needed. @param[in] DimmPid Dimm ID of DIMM from which to read the data @param[out] ppLsa Pointer with address at which memory LSA data will be stored. @retval EFI_INVALID_PARAMETER NULL pointer provided as a parameter @retval EFI_DEVICE_ERROR Unable to retrieve data from DIMM or retrieved data is not valid @retval EFI_SUCCESS Valid LSA retrieved **/ EFI_STATUS ReadLabelStorageArea( IN UINT16 DimmPid, OUT LABEL_STORAGE_AREA **ppLsa ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimm = NULL; UINT8 *pRawData = NULL; UINT16 CurrentIndex = 0; UINT64 LabelIndexSize = 0; UINT64 LabelSize = 0; BOOLEAN UseNamespace1_1 = FALSE; UINT8 *pTo = NULL; UINT8 *pFrom = NULL; UINT32 Index = 0; UINT32 IndexSize = 0; UINT32 Offset = 0; UINT32 AlignPageIndex = 0; UINT32 PageSize = 0; UINT8 PageIndexMask = 0; BOOLEAN LargePayloadAvailable = FALSE; NVDIMM_ENTRY(); if (ppLsa == NULL) { goto Finish; } pDimm = GetDimmByPid(DimmPid, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL || !IsDimmManageable(pDimm)) { goto Finish; } NVDIMM_DBG("Reading LSA for DIMM %x ...", pDimm->DeviceHandle.AsUint32); CHECK_RESULT(IsLargePayloadAvailable(pDimm, &LargePayloadAvailable), Finish); if (!LargePayloadAvailable) { // At first read the Index size only form the beginning of the LSA IndexSize = sizeof((*ppLsa)->Index); ReturnCode = FwGetPCDFromOffsetSmallPayload(pDimm, PCD_LSA_PARTITION_ID, Offset, IndexSize, &pRawData); if (EFI_SUCCESS == ReturnCode) { // Read the IndexSize again plus 2 times size of the Free Mask starting at the end of the previous read Offset = IndexSize; IndexSize += 2 * LABELS_TO_FREE_BYTES(ROUNDUP(((LABEL_STORAGE_AREA *)pRawData)->Index[0].NumberOfLabels, NSINDEX_FREE_ALIGN)); ReturnCode = FwGetPCDFromOffsetSmallPayload(pDimm, PCD_LSA_PARTITION_ID, Offset, IndexSize, &pRawData); } } else { ReturnCode = FwCmdGetPlatformConfigData(pDimm, PCD_LSA_PARTITION_ID, &pRawData); } if ((ReturnCode == EFI_NO_MEDIA) || (ReturnCode == EFI_NO_RESPONSE)) { goto Finish; } if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FwCmdGetPlatformConfigData returned: " FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } *ppLsa = AllocateZeroPool(sizeof(**ppLsa)); if (*ppLsa == NULL) { NVDIMM_WARN("Can't allocate memory Label Storage Area"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } // Copy the Label Index area ReturnCode = RawDataToLabelIndexArea(pRawData, pDimm->PcdLsaPartitionSize, *ppLsa); if (EFI_ERROR(ReturnCode)) { goto FinishError; } // Validate the index area ReturnCode = ValidateLsaData(*ppLsa); if (EFI_ERROR(ReturnCode)) { goto FinishError; } ReturnCode = GetLsaIndexes(*ppLsa, &CurrentIndex, NULL); if (EFI_ERROR(ReturnCode)) { goto FinishError; } if ((*ppLsa)->Index[CurrentIndex].Major == NSINDEX_MAJOR && (*ppLsa)->Index[CurrentIndex].Minor == NSINDEX_MINOR_1) { UseNamespace1_1 = TRUE; } LabelIndexSize = NAMESPACE_INDEXES * (*ppLsa)->Index[CurrentIndex].MySize; LabelSize = sizeof(*((*ppLsa)->pLabels)) * (*ppLsa)->Index[CurrentIndex].NumberOfLabels; NVDIMM_DBG("Read from the LSA for DIMM %x: Current index size %d :: No of labels %d", pDimm->DeviceHandle.AsUint32, (*ppLsa)->Index[CurrentIndex].MySize, (*ppLsa)->Index[CurrentIndex].NumberOfLabels); (*ppLsa)->pLabels = AllocateZeroPool(LabelSize); if ((*ppLsa)->pLabels == NULL) { goto FinishError; } // Copy the Label area if (!LargePayloadAvailable) { // Copy the Label area if (UseNamespace1_1) { PageSize = sizeof(NAMESPACE_LABEL_1_1); } else { PageSize = sizeof(NAMESPACE_LABEL); } for (AlignPageIndex = 0; AlignPageIndex < (*ppLsa)->Index[CurrentIndex].NumberOfLabels; AlignPageIndex += NSINDEX_FREE_ALIGN) { // Check if we have any namespaces defined for these slots if ((*ppLsa)->Index[CurrentIndex].pFree[LABELS_TO_FREE_BYTES(AlignPageIndex)] != FREE_BLOCKS_MASK_ALL_SET) { // Find the label to read for(PageIndexMask = (*ppLsa)->Index[CurrentIndex].pFree[LABELS_TO_FREE_BYTES(AlignPageIndex)], Index = 0; (Index < NSINDEX_FREE_ALIGN) && ((AlignPageIndex + Index) < (*ppLsa)->Index[CurrentIndex].NumberOfLabels); PageIndexMask >>= 1, Index++) { if (BIT0 != (PageIndexMask & BIT0)) { // Calculate the offset to read, one label per read only Offset = (UINT32)(LabelIndexSize + (PageSize * (AlignPageIndex + Index))); // Read data ReturnCode = FwGetPCDFromOffsetSmallPayload(pDimm, PCD_LSA_PARTITION_ID, Offset, PageSize, &pRawData); if (EFI_ERROR(ReturnCode)) { goto FinishError; } // Copy data to the LSA struct pFrom = pRawData + Offset; pTo = ((UINT8 *)(*ppLsa)->pLabels) + (sizeof(NAMESPACE_LABEL) * (AlignPageIndex + Index)); CopyMem_S(pTo, PageSize, pFrom, PageSize); } } } } } else { if (UseNamespace1_1) { pTo = (UINT8 *)(*ppLsa)->pLabels; pFrom = pRawData + LabelIndexSize; for (Index = 0; Index < (*ppLsa)->Index[CurrentIndex].NumberOfLabels; Index++) { CopyMem_S(pTo, sizeof(NAMESPACE_LABEL_1_1), pFrom, sizeof(NAMESPACE_LABEL_1_1)); pTo += sizeof(*((*ppLsa)->pLabels)); pFrom += sizeof(NAMESPACE_LABEL_1_1); } } else { CopyMem_S((*ppLsa)->pLabels, LabelSize, pRawData + LabelIndexSize, LabelSize); } } ReturnCode = EFI_SUCCESS; goto Finish; FinishError: FreeLsaSafe(ppLsa); Finish: FREE_POOL_SAFE(pRawData); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Writes Label Storage Area to a specified DIMM. Function invokes validation subroutine to check provided data consistency and then stores LSA on Platform Config Data partition 3. of a DIMM @param[in] DimmPid Dimm ID of DIMM on which to write LSA @param[in] pLsa Pointer with LSA structure @retval EFI_INVALID_PARAMETER No data provided or the data is invalid @retval EFI_DEVICE_ERROR Unable to store data on a DIMM @retval EFI_SUCCESS LSA written correctly **/ EFI_STATUS WriteLabelStorageArea( IN UINT16 DimmPid, IN LABEL_STORAGE_AREA *pLsa ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimm = NULL; UINT8 *pRawData = NULL; UINT64 TotalPcdSize = 0; UINT8 *pTo = NULL; UINT32 Index = 0; UINT64 LabelSize = 0; UINT16 CurrentIndex = 0; UINT64 LabelIndexSize = 0; UINT8 *pIndexArea = NULL; BOOLEAN UseNamespace_1_1 = FALSE; UINT8 *pFrom = NULL; UINT32 AlignPageIndex = 0; UINT32 PageSize = 0; UINT8 PageIndexMask = 0; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS pAttribs; BOOLEAN LargePayloadAvailable = FALSE; NVDIMM_ENTRY(); if (pLsa == NULL) { goto Finish; } ReturnCode = ValidateLsaData(pLsa); if (EFI_ERROR(ReturnCode)) { goto Finish; } pDimm = GetDimmByPid(DimmPid, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL || !IsDimmManageable(pDimm)) { goto Finish; } ReturnCode = GetLsaIndexes(pLsa, &CurrentIndex, NULL); LabelIndexSize = NAMESPACE_INDEXES * pLsa->Index[CurrentIndex].MySize; LabelSize = sizeof(*(pLsa->pLabels)) * pLsa->Index[CurrentIndex].NumberOfLabels; TotalPcdSize = LabelIndexSize + LabelSize; if ((pLsa->Index[CurrentIndex].Major == NSINDEX_MAJOR) && (pLsa->Index[CurrentIndex].Minor == NSINDEX_MINOR_1)) { UseNamespace_1_1 = TRUE; } ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = pNvmDimmConfigProtocol->GetFisTransportAttributes(pNvmDimmConfigProtocol, &pAttribs); if (EFI_ERROR(ReturnCode)) { goto Finish; } CHECK_RESULT(IsLargePayloadAvailable(pDimm, &LargePayloadAvailable), Finish); if (LargePayloadAvailable) { pRawData = AllocateZeroPool(TotalPcdSize); if (pRawData == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } } ReturnCode = LabelIndexAreaToRawData(pLsa, ALL_INDEX_BLOCKS, &pIndexArea); if (EFI_ERROR(ReturnCode) || (pIndexArea == NULL)) { NVDIMM_DBG("Failed to convert label area index to raw data"); goto Finish; } if (!LargePayloadAvailable) { // Copy the Label index area ReturnCode = FwSetPCDFromOffsetSmallPayload(pDimm, PCD_LSA_PARTITION_ID, pIndexArea, 0, (UINT32)LabelIndexSize); if (EFI_ERROR(ReturnCode)) { goto Finish; } // Copy the Label area if (UseNamespace_1_1) { PageSize = sizeof(NAMESPACE_LABEL_1_1); } else { PageSize = sizeof(NAMESPACE_LABEL); } for (AlignPageIndex = 0; AlignPageIndex < pLsa->Index[CurrentIndex].NumberOfLabels; AlignPageIndex += NSINDEX_FREE_ALIGN) { // Check if we have at least one namespace to copy if (pLsa->Index[CurrentIndex].pFree[LABELS_TO_FREE_BYTES(AlignPageIndex)] != FREE_BLOCKS_MASK_ALL_SET) { // Find the label to write for (PageIndexMask = pLsa->Index[CurrentIndex].pFree[LABELS_TO_FREE_BYTES(AlignPageIndex)], Index = 0; (Index < NSINDEX_FREE_ALIGN) && ((AlignPageIndex + Index) < pLsa->Index[CurrentIndex].NumberOfLabels); PageIndexMask >>= 1, Index++) { if (BIT0 != (PageIndexMask & BIT0)) { // Calculate the offset to write, one label per write only pFrom = ((UINT8 *)(pLsa->pLabels) + (sizeof(NAMESPACE_LABEL) * (AlignPageIndex + Index))); ReturnCode = FwSetPCDFromOffsetSmallPayload(pDimm, PCD_LSA_PARTITION_ID, pFrom, (UINT32)(LabelIndexSize + (PageSize * (AlignPageIndex + Index))), PageSize); if (EFI_ERROR(ReturnCode)) { goto Finish; } } } } } } else { // Copy the Label index area, but check for NULL first CHECK_NOT_TRUE((NULL != pRawData && NULL != pIndexArea), Finish); CopyMem_S(pRawData, TotalPcdSize, pIndexArea, LabelIndexSize); // Copy the label area if (UseNamespace_1_1) { pFrom = (UINT8 *)pLsa->pLabels; pTo = pRawData + LabelIndexSize; for (Index = 0; Index < pLsa->Index[CurrentIndex].NumberOfLabels; Index++) { CopyMem_S(pTo, sizeof(NAMESPACE_LABEL_1_1), pFrom, sizeof(NAMESPACE_LABEL_1_1)); pFrom += sizeof(*(pLsa->pLabels)); pTo += sizeof(NAMESPACE_LABEL_1_1); } } else { CopyMem_S(pRawData + LabelIndexSize, TotalPcdSize - LabelIndexSize, pLsa->pLabels, LabelSize); } NVDIMM_DBG("Writing LSA to DIMM %x ...", pDimm->DeviceHandle.AsUint32); ReturnCode = FwCmdSetPlatformConfigData(pDimm, PCD_LSA_PARTITION_ID, pRawData, pDimm->PcdLsaPartitionSize); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FwCmdSetPlatformConfigData returned: " FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } } Finish: FREE_POOL_SAFE(pIndexArea); FREE_POOL_SAFE(pRawData); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Zero the Label Storage Area Header on the specified DIMM. @param[in] DimmPid Dimm ID of DIMM on which to write LSA @retval EFI_INVALID_PARAMETER No data provided or the data is invalid @retval EFI_OUT_RESOURCES Unable to allocate resources @retval EFI_SUCCESS LSA written correctly **/ EFI_STATUS ZeroLabelStorageAreaHeader( IN UINT16 DimmPid ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimm = NULL; UINT8 *pZeroRawLsaHeader = NULL; // 2 LSA header index blocks corresponding to 256KB partitions. We only // have 128KB partitions in PMem (256B index blocks), but rounding up just in case. CONST UINT16 BytesToZero = 2 * 512; NVDIMM_ENTRY(); pDimm = GetDimmByPid(DimmPid, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL || !IsDimmManageable(pDimm)) { goto Finish; } pZeroRawLsaHeader = AllocateZeroPool(BytesToZero); if (pZeroRawLsaHeader == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } NVDIMM_DBG("Zero-ing the LSA header on DIMM 0x%x ...", pDimm->DeviceHandle.AsUint32); ReturnCode = FwCmdSetPlatformConfigData(pDimm, PCD_LSA_PARTITION_ID, pZeroRawLsaHeader, BytesToZero); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FwCmdSetPlatformConfigData returned: " FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } Finish: FREE_POOL_SAFE(pZeroRawLsaHeader); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** GetNamespace Looks through the list of namespaces, searching for a namespace with a particular GUID. @param[in] pNamespacesList pointer to the list where the function is supposed to search for the Namespace. @param[in] Uuid the target UUID of the Namespace that the function is searching for. @param[out] ppNamespace pointer to a pointer of where the Namespace pointer will be stored. @retval FALSE At least one of the input pointers is NULL or there is no Namespace with the specified UUID on the list. @retval TRUE the Namespace has been found and is stored under the ppNamespace. **/ STATIC BOOLEAN GetNamespace( IN LIST_ENTRY *pNamespacesList, IN GUID Uuid, OUT NAMESPACE **ppNamespace ) { LIST_ENTRY *pNamespaceNode = NULL; NAMESPACE *pNamespace = NULL; if (pNamespacesList == NULL || ppNamespace == NULL) { return FALSE; } for (pNamespaceNode = GetFirstNode(&gNvmDimmData->PMEMDev.Namespaces); !IsNull(&gNvmDimmData->PMEMDev.Namespaces, pNamespaceNode); pNamespaceNode = GetNextNode(&gNvmDimmData->PMEMDev.Namespaces, pNamespaceNode)) { pNamespace = NAMESPACE_FROM_NODE(pNamespaceNode, NamespaceNode); if (CompareMem(pNamespace->NamespaceGuid, &Uuid, sizeof(pNamespace->NamespaceGuid)) == 0) { // We have a match! *ppNamespace = pNamespace; return TRUE; } } return FALSE; } /** GetNamespaceByName Looks through namespaces list searching for a namespace with a particular name. @param[in] pName Target Name of the Namespace that the function is searching for. @retval NAMESPACE structure pointer if Namespace has been found @retval NULL pointer if not found **/ NAMESPACE* GetNamespaceByName( IN CHAR8 *pName ) { LIST_ENTRY *pNode = NULL; NAMESPACE *pNamespace = NULL; NAMESPACE *pTargetNamespace = NULL; if (pName == NULL) { goto Finish; } LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Namespaces) { pNamespace = NAMESPACE_FROM_NODE(pNode, NamespaceNode); if (CompareMem(pNamespace->Name, pName, sizeof(pNamespace->Name)) == 0) { pTargetNamespace = pNamespace; goto Finish; } } Finish: return pTargetNamespace; } /** Looks through namespaces list searching for a namespace with a particular id. @param[in] NamespaceId Target ID of the Namespace that the function is searching for. @retval NAMESPACE structure pointer if Namespace has been found @retval NULL pointer if not found **/ NAMESPACE* GetNamespaceById( IN UINT16 NamespaceId ) { LIST_ENTRY *pNode = NULL; NAMESPACE *pNamespace = NULL; NAMESPACE *pTargetNamespace = NULL; LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Namespaces) { pNamespace = NAMESPACE_FROM_NODE(pNode, NamespaceNode); if (pNamespace->NamespaceId == NamespaceId) { pTargetNamespace = pNamespace; goto Finish; } } Finish: return pTargetNamespace; } /** Function checks if first data of a Namespace is a BTT Arena Info Block @param[in] pNamespace Namespace to be verified @param[out] pBttFound BTT existence flag @retval EFI_SUCCESS Verification passed @retval EFI_OUT_OF_RESOURCES Memory allocation failed @retval EFI_VOLUME_CORRUPTED BTT found but is inconsistent @retval EFI_INVALID_PARAMETER One of input parameters is NULL **/ EFI_STATUS CheckBttExistence( IN NAMESPACE *pNamespace, OUT BOOLEAN *pBttFound ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; BTT_INFO *pBttInfo = NULL; UINT64 BttInfoOffset = BTT_PRIMARY_INFO_BLOCK_OFFSET; NVDIMM_ENTRY(); if (pNamespace == NULL || pBttFound == NULL) { goto Finish; } if ((pNamespace->Major == NSINDEX_MAJOR) && (pNamespace->Minor == NSINDEX_MINOR_1)) { BttInfoOffset = BTT_PRIMARY_INFO_BLOCK_OFFSET_1_1; } *pBttFound = FALSE; pBttInfo = AllocateZeroPool(sizeof(BTT_INFO)); if (pBttInfo == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } // Check primary BTT info block of first arena ReturnCode = ReadNamespaceBytes(pNamespace, BttInfoOffset, pBttInfo, sizeof(BTT_INFO)); if(EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to read namespace bytes"); goto Finish; } ReturnCode = BttReadInfo(pBttInfo, pNamespace->pBtt); if(!EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Valid primary BTT_INFO block found."); *pBttFound = TRUE; } // Check backup BTT info block of first arena if primary info block wasn't found // Currently no recovery occurs, we just detect the backup info block here. if (*pBttFound == FALSE) { if (GetAccessibleCapacity(pNamespace) > BTT_MAX_ARENA_SIZE) { BttInfoOffset = BTT_MAX_ARENA_SIZE - sizeof(BTT_INFO); } else { BttInfoOffset = GetAccessibleCapacity(pNamespace) - sizeof(BTT_INFO); } ReturnCode = ReadNamespaceBytes(pNamespace, BttInfoOffset, pBttInfo, sizeof(BTT_INFO)); if(EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to read namespace bytes"); goto Finish; } ReturnCode = BttReadInfo(pBttInfo, pNamespace->pBtt); if(!EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Valid backup BTT_INFO block found but recovery is not supported."); *pBttFound = TRUE; } } // BTT GUID must match parent (Namespace) GUID if (*pBttFound == TRUE && CompareMem(&pNamespace->NamespaceGuid, &pBttInfo->ParentUuid, sizeof(GUID)) != 0) { NVDIMM_DBG("BTT and Namespace GUID don't match:"); NVDIMM_DBG("BTT Parent GUID: %g", pBttInfo->ParentUuid); NVDIMM_DBG("NS GUID: %g", pNamespace->NamespaceGuid); *pBttFound = FALSE; } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pBttInfo); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Function checks if first data of a Namespace is a PFN info block @param[in] pNamespace Namespace to be verified @param[out] pPfnFound PFN existence flag @retval EFI_SUCCESS Verification passed @retval EFI_OUT_OF_RESOURCES Memory allocation failed @retval EFI_VOLUME_CORRUPTED PFN found but is inconsistent @retval EFI_INVALID_PARAMETER One of input parameters is NULL **/ EFI_STATUS CheckPfnExistence( IN NAMESPACE *pNamespace, OUT BOOLEAN *pPfnFound ) { PFN_INFO *pPfnInfo = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pNamespace == NULL || pPfnFound == NULL) { goto Finish; } *pPfnFound = FALSE; pPfnInfo = AllocateZeroPool(sizeof(PFN_INFO)); if (pPfnInfo == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = ReadNamespaceBytes(pNamespace, PFN_INFO_BLOCK_OFFSET, pPfnInfo, sizeof(PFN_INFO)); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to read namespace bytes"); goto Finish; } ReturnCode = PfnValidateInfo(pPfnInfo, NULL); if (!EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Valid PFN_INFO block found."); *pPfnFound = TRUE; } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pPfnInfo); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #if 0 // This flow may not work properly under certain scenarios. /** Recover a partially updated namespace label set Clear the updating bit and use the name from label in pos 0 @param[in] pUuid of the label to perform recovery on @param[in out] pNamespace - namespace struct that needs to be updated after recovery @param[in out] pNamespaceLabelStale - namespace label struct that needs to be updated after recovery **/ STATIC EFI_STATUS RecoverLabelSet( IN GUID *pUuid, IN OUT NAMESPACE *pNamespace, IN OUT NAMESPACE_LABEL *pNamespaceLabelStale ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimm = NULL; NAMESPACE_LABEL *pNamespaceLabel = NULL; LIST_ENTRY *pNode = NULL; CHAR8 *pNamespaceLabelName = NULL; LABEL_FLAGS Flags; UINT32 LabelsFound = 0; BOOLEAN Pos0Found = FALSE; NVDIMM_ENTRY(); ZeroMem(&Flags, sizeof(Flags)); if (pNamespace == NULL || pNamespaceLabelStale == NULL) { goto Finish; } pNamespaceLabelName = AllocateZeroPool(NLABEL_NAME_LEN_WITH_TERMINATOR); if (pNamespaceLabelName == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } // Find position 0 and store the name LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pNode); if (!IsDimmManageable(pDimm)) { continue; } ReturnCode = GetAppDirectLabelByUUID(pDimm, *pUuid, &pNamespaceLabel); if (ReturnCode == EFI_NOT_FOUND || ReturnCode == EFI_NO_MEDIA) { continue; } if (EFI_ERROR(ReturnCode)) { FREE_POOL_SAFE(pNamespaceLabel); goto Finish; } LabelsFound++; if (pNamespaceLabel->Position == 0) { CopyMem_S(pNamespaceLabelName, NLABEL_NAME_LEN_WITH_TERMINATOR, &pNamespaceLabel->Name, NSLABEL_NAME_LEN); Pos0Found = TRUE; } FREE_POOL_SAFE(pNamespaceLabel); } // Check if the set is complete if (pNamespaceLabelStale->NumberOfLabels != LabelsFound) { NVDIMM_DBG("Invalid number of labels found! Expected %d, got %d", pNamespaceLabelStale->NumberOfLabels, LabelsFound); ReturnCode = EFI_ABORTED; goto Finish; } // Check if position 0 was found if (!Pos0Found) { NVDIMM_DBG("Could not find Position 0 label."); ReturnCode = EFI_ABORTED; goto Finish; } // Write each label label in the set with pos 0 name and UPDATING clear LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pNode); if (!IsDimmManageable(pDimm)) { continue; } ReturnCode = GetAppDirectLabelByUUID(pDimm, *pUuid, &pNamespaceLabel); if (ReturnCode == EFI_NOT_FOUND) { continue; } if (EFI_ERROR(ReturnCode)) { FREE_POOL_SAFE(pNamespaceLabel); goto Finish; } // Clear the UPDATING bit Flags.AsUint32 = pNamespaceLabel->Flags.AsUint32; Flags.Values.Updating = 0; ReturnCode = ModifyNamespaceLabels(pDimm, pUuid, &Flags.AsUint32, pNamespaceLabelName, 0); if (EFI_ERROR(ReturnCode)) { FREE_POOL_SAFE(pNamespaceLabel); goto Finish; } FREE_POOL_SAFE(pNamespaceLabel); } // Our structs for pNamespace and pNamespaceLabel in RetrieveNamespacesFromLsa are now out of date // Update the UPDATING bit and name here saving another LSA Storage read pNamespace->Flags.Values.Updating = 0; CopyMem_S(&pNamespace->Name, sizeof(pNamespace->Name), pNamespaceLabelName, NSLABEL_NAME_LEN); pNamespaceLabelStale->Flags.Values.Updating = 0; CopyMem_S(&pNamespaceLabelStale->Name, sizeof(pNamespaceLabelStale->Name), pNamespaceLabelName, NSLABEL_NAME_LEN); ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pNamespaceLabelName); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif /** Retrieve Namespaces information from provided LSA structure. Function scans Namespaces Index of LSA and reads Namespace slots for Namespace data. Memory for required structures is allocated and it is callee responsibility to free it when it is no longer needed. @param[in] pDimm Pointer to a DIMM structure to which LSA relates @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[out] pNamespacesList Pointer to a list to which to add Namespace structures @retval EFI_INVALID_PARAMETER No data provided or the data is invalid @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval EFI_SUCCESS Namespaces correctly retrieved **/ EFI_STATUS RetrieveNamespacesFromLsa( IN DIMM *pDimm, IN ParsedFitHeader *pFitHead, OUT LIST_ENTRY *pNamespacesList ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_STATUS ReturnCode2 = EFI_INVALID_PARAMETER; NAMESPACE_LABEL *pNamespaceLabel = NULL; NAMESPACE_LABEL *pNamespaceLabel2 = NULL; NAMESPACE *pNamespace = NULL; LIST_ENTRY *pNode = NULL; BOOLEAN ChecksumMatch = FALSE; UINT16 CurrentIndex = 0; UINT16 SlotStatus = SLOT_UNKNOWN; UINT32 Index = 0; UINT32 LabelsFound = 0; BOOLEAN BttFound = FALSE; BOOLEAN PfnFound = FALSE; UINT64 RawCapacity = 0; BOOLEAN Use_Namespace1_1 = FALSE; EFI_GUID ZeroGuid; LABEL_STORAGE_AREA *pLsa = NULL; NVDIMM_ENTRY(); ZeroMem(&ZeroGuid, sizeof(EFI_GUID)); if (pNamespacesList == NULL) { NVDIMM_DBG("pNamespacesList is NULL"); goto Finish; } if (pDimm == NULL) { NVDIMM_DBG("pDimm is NULL"); goto Finish; } pLsa = pDimm->pLsa; if (pLsa == NULL) { NVDIMM_DBG("pLsa is NULL"); goto Finish; } ReturnCode = GetLsaIndexes(pLsa, &CurrentIndex, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (pLsa->Index[CurrentIndex].Major == NSINDEX_MAJOR && pLsa->Index[CurrentIndex].Minor == NSINDEX_MINOR_1) { Use_Namespace1_1 = TRUE; } for (Index = 0; Index < pLsa->Index[CurrentIndex].NumberOfLabels; Index++) { RawCapacity = 0; CheckSlotStatus(&pLsa->Index[CurrentIndex], (UINT16)Index, &SlotStatus); if (SlotStatus == SLOT_FREE) { continue; } NVDIMM_DBG("Label found at slot %d", Index); pNamespaceLabel = &pLsa->pLabels[Index]; if (GetNamespace(pNamespacesList, pNamespaceLabel->Uuid, &pNamespace)) { NVDIMM_DBG("Namespace for this label already initialized"); continue; } ReturnCode = ValidateNamespaceLabel(pNamespaceLabel, Use_Namespace1_1); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Label invalid or check failed. Skipping"); continue; } pNamespace = (NAMESPACE *) AllocateZeroPool(sizeof(*pNamespace)); if (pNamespace == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pNamespace->Flags.AsUint32 = pNamespaceLabel->Flags.AsUint32; pNamespace->Signature = NAMESPACE_SIGNATURE; pNamespace->Enabled = FALSE; pNamespace->HealthState = NAMESPACE_HEALTH_OK; CopyMem_S(&pNamespace->Name, sizeof(pNamespace->Name), &pNamespaceLabel->Name, NSLABEL_NAME_LEN); CopyMem_S(&pNamespace->NamespaceGuid, sizeof(pNamespace->NamespaceGuid), &pNamespaceLabel->Uuid, NSGUID_LEN); pNamespace->InterleaveSetCookie = pNamespaceLabel->InterleaveSetCookie; pNamespace->Major = NSINDEX_MAJOR; if (Use_Namespace1_1) { pNamespace->Minor = NSINDEX_MINOR_1; } else { pNamespace->Minor = NSINDEX_MINOR_2; } LabelsFound = 0; if (IsNameSpaceTypeAppDirect(pNamespaceLabel, Use_Namespace1_1)) { if (!Use_Namespace1_1 && !(CompareGuid(&gSpaRangeIsoPmRegionGuid, &pNamespaceLabel->TypeGuid) || CompareGuid(&gSpaRangeVolatileRegionGuid, &pNamespaceLabel->TypeGuid) || CompareGuid(&gSpaRangeIsoVolatileRegionGuid, &pNamespaceLabel->TypeGuid) || CompareGuid(&gSpaRangePmRegionGuid, &pNamespaceLabel->TypeGuid) || CompareGuid(&gSpaRangeRawPmRegionGuid, &pNamespaceLabel->TypeGuid) || CompareGuid(&gSpaRangeRawVolatileRegionGuid, &pNamespaceLabel->TypeGuid))) { NVDIMM_DBG("Unexpected TypeGuid for AppDirect NS"); continue; } #if 0 // This flow is incomplete and may result in corrupt LSA // OSV's advise preference for UEFI to skip any automated recovery // Leave recovery to the OS // Logic after still validates the labels are consistent // Iterate over DIMMs to check for partial update LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pNode); if (!IsDimmManageable(pDimm)) { continue; } ReturnCode = GetAppDirectLabelByUUID(pDimm, pNamespaceLabel->Uuid, &pNamespaceLabel2); if (ReturnCode == EFI_NOT_FOUND) { continue; } if (EFI_ERROR(ReturnCode)) { FREE_POOL_SAFE(pNamespaceLabel2); break; } if (pNamespaceLabel2->Flags.Values.Updating) { NVDIMM_WARN("Partial update of namespace labels detected. Performing recovery."); ReturnCode = RecoverLabelSet(&pNamespaceLabel->Uuid, pNamespace, pNamespaceLabel); if (EFI_ERROR(ReturnCode)) { FREE_POOL_SAFE(pNamespaceLabel2); NVDIMM_WARN("Failed to recover namespace labels."); pNamespace->HealthState = NAMESPACE_HEALTH_CRITICAL; break; } } FREE_POOL_SAFE(pNamespaceLabel2); } #endif // Iterate over DIMMs to collect labels to assemble AppDirect NS LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pNode); if (!IsDimmManageable(pDimm)) { continue; } ReturnCode = GetAppDirectLabelByUUID(pDimm, pNamespaceLabel->Uuid, &pNamespaceLabel2); if (ReturnCode == EFI_NOT_FOUND || ReturnCode == EFI_NO_MEDIA) { continue; } if (EFI_ERROR(ReturnCode)) { FREE_POOL_SAFE(pNamespaceLabel2); goto Finish; } // Check labels consistency if (pNamespaceLabel->Flags.AsUint32 != pNamespaceLabel2->Flags.AsUint32 || pNamespaceLabel->LbaSize != pNamespaceLabel2->LbaSize || CompareMem(pNamespaceLabel->Name, pNamespaceLabel2->Name, sizeof(pNamespaceLabel->Name)) != 0 || pNamespace->InterleaveSetCookie != pNamespaceLabel2->InterleaveSetCookie) { FREE_POOL_SAFE(pNamespaceLabel2); NVDIMM_DBG("AppDirect Namespace labels are not consistent. Skipping the current label."); continue; } // Only support 512 or 4k LbaSize // Linux treats 0 as 512 if (pNamespaceLabel2->LbaSize == 0 || pNamespaceLabel2->LbaSize == AD_NAMESPACE_LABEL_LBA_SIZE_512) { pNamespace->Media.BlockSize = AD_NAMESPACE_LABEL_LBA_SIZE_512; } else if (pNamespaceLabel2->LbaSize == AD_NAMESPACE_LABEL_LBA_SIZE_4K) { pNamespace->Media.BlockSize = AD_NAMESPACE_LABEL_LBA_SIZE_4K; } else { NVDIMM_WARN("Unsupported LbaSize %lld", pNamespaceLabel2->LbaSize); pNamespace->Media.BlockSize = AD_NAMESPACE_LABEL_LBA_SIZE_512; pNamespace->HealthState = NAMESPACE_HEALTH_WARNING; } ReturnCode = ValidateNamespaceLabel(pNamespaceLabel2, Use_Namespace1_1); if (EFI_ERROR(ReturnCode)) { FREE_POOL_SAFE(pNamespaceLabel2); NVDIMM_DBG("Label invalid or check failed. Skipping"); continue; } ChecksumMatch = ChecksumOperations(pNamespaceLabel2, sizeof(*pNamespaceLabel2), &pNamespaceLabel2->Checksum, FALSE); if (!Use_Namespace1_1 && !ChecksumMatch) { FREE_POOL_SAFE(pNamespaceLabel2); pNamespace->HealthState = NAMESPACE_HEALTH_CRITICAL; continue; } if (!Use_Namespace1_1 && !(CompareGuid(&gSpaRangeIsoPmRegionGuid, &pNamespaceLabel2->TypeGuid) || CompareGuid(&gSpaRangeVolatileRegionGuid, &pNamespaceLabel2->TypeGuid) || CompareGuid(&gSpaRangeIsoVolatileRegionGuid, &pNamespaceLabel2->TypeGuid) || CompareGuid(&gSpaRangePmRegionGuid, &pNamespaceLabel2->TypeGuid) || CompareGuid(&gSpaRangeRawPmRegionGuid, &pNamespaceLabel2->TypeGuid) || CompareGuid(&gSpaRangeRawVolatileRegionGuid, &pNamespaceLabel2->TypeGuid))) { FREE_POOL_SAFE(pNamespaceLabel2); NVDIMM_DBG("Unexpected TypeGuid for AppDirect NS"); continue; } if (!Use_Namespace1_1) { NVDIMM_DBG("Check Abstraction GUID: %g", pNamespaceLabel2->AddressAbstractionGuid); if (CompareGuid(&gBttAbstractionGuid, &pNamespaceLabel2->AddressAbstractionGuid)) { pNamespace->IsBttEnabled = TRUE; } else if (CompareGuid(&gPfnAbstractionGuid, &pNamespaceLabel2->AddressAbstractionGuid)) { pNamespace->IsPfnEnabled = TRUE; } else if (CompareGuid(&ZeroGuid, &pNamespaceLabel2->AddressAbstractionGuid)) { pNamespace->IsRawNamespace = TRUE; } } // Save an extra FIS call in OS. OS just needs to size for region capacity used #ifndef OS_BUILD UINT32 SecurityState = 0; ReturnCode = GetDimmSecurityState(pDimm, PT_TIMEOUT_INTERVAL, &SecurityState); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to get DIMM security state."); goto Finish; } if (!IsConfiguringAllowed(SecurityState)) { NVDIMM_DBG("Namespace contains a locked DIMM 0x%X", pDimm->DeviceHandle.AsUint32); pNamespace->HealthState = NAMESPACE_HEALTH_LOCKED; } #endif LabelsFound++; NVDIMM_DBG("Label (%d/%d) of App Direct Namespace %g foundDPA = 0x%lx", LabelsFound, pNamespaceLabel->NumberOfLabels, pNamespace->NamespaceGuid, pNamespaceLabel2->Dpa); pNamespace->Range[pNamespace->RangesCount].Dpa = pNamespaceLabel2->Dpa; pNamespace->Range[pNamespace->RangesCount].Size = pNamespaceLabel2->RawSize; pNamespace->Range[pNamespace->RangesCount].pDimm = pDimm; pNamespace->RangesCount += 1; RawCapacity += pNamespaceLabel2->RawSize; FREE_POOL_SAFE(pNamespaceLabel2); } pNamespace->BlockSize = AD_NAMESPACE_BLOCK_SIZE; pNamespace->BlockCount = RawCapacity; pNamespace->UsableSize = RawCapacity; pNamespace->NamespaceType = APPDIRECT_NAMESPACE; // Find parent Interleave Set ReturnCode = FindAndAssignISForNamespace(pNamespace); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Interleave Set has not been found for App Direct Namespace."); pNamespace->HealthState = NAMESPACE_HEALTH_CRITICAL; } if (pNamespace->pParentIS != NULL && pNamespace->HealthState != NAMESPACE_HEALTH_OK) { if (pNamespace->pParentIS->State != IS_STATE_HEALTHY) { switch (pNamespace->pParentIS->State) { case IS_STATE_INIT_FAILURE: case IS_STATE_DIMM_MISSING: pNamespace->HealthState = NAMESPACE_HEALTH_CRITICAL; break; case IS_STATE_CONFIG_INACTIVE: case IS_STATE_SPA_MISSING: default: pNamespace->HealthState = NAMESPACE_HEALTH_CRITICAL; break; } } } else if (pNamespace->pParentIS == NULL) { // We will hit this case if there are no IS in the system or if we couldn't find any IS // with a valid cookie to match the labels. FREE_POOL_SAFE(pNamespace); continue; } // Sanity check if (pNamespaceLabel->NumberOfLabels != LabelsFound) { NVDIMM_DBG("Invalid number of labels found! Expected %d, got %d", pNamespaceLabel->NumberOfLabels, LabelsFound); pNamespace->HealthState = NAMESPACE_HEALTH_CRITICAL; pNamespace->Enabled = FALSE; } } // Get the Index of the last namespace Id for the previous interleave set pNamespace->NamespaceId = CREATE_NAMESPACE_ID(pNamespace->pParentIS->RegionId, Index); InsertTailList(pNamespacesList, &pNamespace->NamespaceNode); if (pNamespace->HealthState != NAMESPACE_HEALTH_CRITICAL && pNamespace->HealthState != NAMESPACE_HEALTH_UNSUPPORTED && pNamespace->HealthState != NAMESPACE_HEALTH_LOCKED) { if (Use_Namespace1_1 || pNamespace->IsBttEnabled || pNamespace->IsPfnEnabled) { ReturnCode = EFI_INVALID_PARAMETER; ReturnCode2 = EFI_INVALID_PARAMETER; #ifndef OS_BUILD BttFound = FALSE; PfnFound = FALSE; if (Use_Namespace1_1 || pNamespace->IsBttEnabled) { ReturnCode = CheckBttExistence(pNamespace, &BttFound); } if (Use_Namespace1_1 || pNamespace->IsPfnEnabled) { ReturnCode2 = CheckPfnExistence(pNamespace, &PfnFound); } #endif if ((EFI_ERROR(ReturnCode) && EFI_ERROR(ReturnCode2)) || (BttFound && PfnFound)) { NVDIMM_DBG("Failed to check address abstraction existence"); pNamespace->HealthState = NAMESPACE_HEALTH_CRITICAL; } if (BttFound) { NVDIMM_DBG("BTT detected"); pNamespace->IsBttEnabled = TRUE; } else if (PfnFound) { NVDIMM_DBG("PFN detected"); pNamespace->IsPfnEnabled = TRUE; } else { NVDIMM_DBG("Address abstraction not detected"); pNamespace->IsBttEnabled = FALSE; pNamespace->IsPfnEnabled = FALSE; pNamespace->IsRawNamespace = TRUE; if (!Use_Namespace1_1) { // Set health state critical only for v1.2 pNamespace->HealthState = NAMESPACE_HEALTH_CRITICAL; } } if (pNamespace->IsBttEnabled) { pNamespace->pBtt = BttInit( GetAccessibleCapacity(pNamespace), (UINT32)GetBlockDeviceBlockSize(pNamespace), (GUID *) pNamespace->NamespaceGuid, pNamespace ); if (pNamespace->pBtt == NULL) { NVDIMM_DBG("Failed to initialize the BTT. Namespace GUID: %g", (GUID *)pNamespace->NamespaceGuid); pNamespace->HealthState = NAMESPACE_HEALTH_CRITICAL; } } else if (pNamespace->IsPfnEnabled) { ReturnCode = PfnInit( GetAccessibleCapacity(pNamespace), (UINT32)GetBlockDeviceBlockSize(pNamespace), (GUID *) pNamespace->NamespaceGuid, pNamespace ); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to initialize the PFN. Namespace GUID: %g", (GUID *)pNamespace->NamespaceGuid); pNamespace->HealthState = NAMESPACE_HEALTH_CRITICAL; } } } } if (pNamespace->NamespaceType == APPDIRECT_NAMESPACE && ( pNamespace->HealthState == NAMESPACE_HEALTH_OK || pNamespace->HealthState == NAMESPACE_HEALTH_WARNING ) ) { pNamespace->Enabled = TRUE; } } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Checks whether NamespaceType is AppDirect Function reads the label version and NameSpaceLabel and determines whether App Direct Type or not @retval TRUE if AppDirect Type @retval FALSE if not AppDirect Type **/ BOOLEAN IsNameSpaceTypeAppDirect(IN NAMESPACE_LABEL *pNamespaceLabel, IN BOOLEAN Is_Namespace1_1 ) { if (!Is_Namespace1_1) return CompareGuid(&gAppDirectPmTypeGuid, &pNamespaceLabel->TypeGuid); else return !(pNamespaceLabel->Flags.Values.Local & 0x01); } /* Checks if Lsa status of Dimms is not initialized for a manageable dimm @retval TRUE - if a manageable dimm has lsaStatus set to LSA_NOT_INIT */ BOOLEAN IsLsaNotInitializedOnADimm() { LIST_ENTRY *pNode = NULL; DIMM *pDimm = NULL; BOOLEAN ReturnCode = FALSE; LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pNode); if (!IsDimmManageable(pDimm)) { continue; } ReturnCode |= (LSA_NOT_INIT == pDimm->LsaStatus); } return ReturnCode; } /** Initializes Namespaces inventory Function reads LSA data from all DIMMs, then scans for Namespaces data in it. All found Namespaces are stored in a list in global gNvmDimmData->PMEMDev structure. If any DCPMMs fail to initialize, continue to initialize the rest of them, but return an error. @retval EFI_DEVICE_ERROR Reading LSA data failed @retval EFI_ABORTED Reading Namespaces data from LSA failed @retval EFI_SUCCESS Namespaces inventory correctly initialized **/ EFI_STATUS InitializeNamespaces( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TempReturnCode = EFI_INVALID_PARAMETER; LIST_ENTRY *pNode = NULL; DIMM *pDimm = NULL; LABEL_STORAGE_AREA *pLsa = NULL; NVDIMM_ENTRY(); LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pNode); if (pDimm->pLsa != NULL) { FreeLsaSafe(&pDimm->pLsa); pDimm->pLsa = NULL; } if (!IsDimmManageable(pDimm) || DIMM_MEDIA_NOT_ACCESSIBLE(pDimm->BootStatusBitmask)) { continue; } TempReturnCode = ReadLabelStorageArea(pDimm->DimmID, &pLsa); if (TempReturnCode == EFI_NOT_FOUND) { /** Return code is purposefully not set here. EFI_NOT_FOUND is returned due to LSA not being initialized. In this case success code should be returned **/ NVDIMM_DBG("LSA not found on DIMM 0x%x", pDimm->DeviceHandle.AsUint32); pDimm->LsaStatus = LSA_NOT_INIT; continue; } else if (TempReturnCode == EFI_NO_RESPONSE) { ReturnCode = TempReturnCode; NVDIMM_DBG("DIMM 0x%x did not respond to attempt to read LSA", pDimm->DeviceHandle.AsUint32); goto Finish; } else if (EFI_ERROR(TempReturnCode)) { ReturnCode = TempReturnCode; pDimm->LsaStatus = LSA_CORRUPTED; /** If the LSA is corrupted, we do nothing - it may be a driver mismatch between UEFI and the OS, so we don't want to "kill" a valid configuration **/ NVDIMM_DBG("LSA corrupted on DIMM 0x%x", pDimm->DeviceHandle.AsUint32); continue; } pDimm->pLsa = pLsa; } LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pNode); if (!IsDimmManageable(pDimm) || DIMM_MEDIA_NOT_ACCESSIBLE(pDimm->BootStatusBitmask)) { continue; } if (pDimm->LsaStatus == LSA_NOT_INIT || pDimm->LsaStatus == LSA_CORRUPTED) { continue; } TempReturnCode = RetrieveNamespacesFromLsa(pDimm, gNvmDimmData->PMEMDev.pFitHead, &gNvmDimmData->PMEMDev.Namespaces); if (EFI_ERROR(TempReturnCode)) { ReturnCode = TempReturnCode; NVDIMM_DBG("Failed to retrieve Namespaces from LSA"); pDimm->LsaStatus = LSA_COULD_NOT_READ_NAMESPACES; continue; } pDimm->LsaStatus = LSA_OK; } Finish: //cleanup cache LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pNode); if (pDimm->pLsa != NULL) { FreeLsaSafe(&pDimm->pLsa); pDimm->pLsa = NULL; } } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Read data from an Intel NVM Dimm Namespace. Transform LBA into RDPA and call the Intel NVM Dimm read function. The function reads the block size, so the input buffer size needs to be at least the namespace block size. @param[in] pNamespace The Intel NVM Dimm Namespace to read data from @param[in] Lba LBA of the start of the data region to read @param[out] pBuffer Buffer to place read data into @retval EFI_SUCCESS on a successful read @retval Error return values from IoNamespaceBlock function **/ EFI_STATUS ReadNamespaceBlock( IN NAMESPACE *pNamespace, IN UINT64 Lba, OUT CHAR8 *pBuffer ) { return IoNamespaceBlock(pNamespace, Lba, pBuffer, (UINT32)pNamespace->Media.BlockSize, TRUE); } /** Write data to an Intel NVM Dimm Namespace. Transform LBA into RDPA and call the Intel NVM Dimm DIMM write function. The function writes the block size, so the input buffer size needs to be at least the namespace block size. @param[in] pNamespace The Intel NVM Dimm Namespace to write data to @param[in] Lba LBA of the start of the data region to write @param[in] pBuffer Buffer with data to write @retval EFI_SUCCESS on a successful write @retval Error return values from IoNamespaceBlock function **/ EFI_STATUS WriteNamespaceBlock( IN NAMESPACE *pNamespace, IN UINT64 Lba, IN CHAR8 *pBuffer ) { return IoNamespaceBlock(pNamespace, Lba, pBuffer, (UINT32)pNamespace->Media.BlockSize, FALSE); } /** Read data from an Intel NVM Dimm Namespace. Transform buffer offset into RDPA. Call the Intel NVM Dimm read function. The buffer length and the offset need to be aligned to the cache line size. @param[in] pNamespace The Intel NVM Dimm Namespace to read data from @param[in] Offset bytes offset of the start of the data region to read @param[in] Length the length of the buffer to read @param[out] pBuffer Buffer to place read data into @retval EFI_SUCCESS on a successful read @retval Error return values from IoNamespaceBlock function **/ EFI_STATUS ReadNamespaceBytes( IN NAMESPACE *pNamespace, IN CONST UINT64 Offset, OUT VOID *pBuffer, IN CONST UINT64 Length ) { return IoNamespaceBytes(pNamespace, Offset, (CHAR8 *)pBuffer, (UINT32)Length, TRUE); } /** Write data to an Intel NVM Dimm Namespace. Transform buffer offset into RDPA. Call the Intel NVM Dimm write function. The buffer length and the offset need to be aligned to the cache line size. @param[in] pNamespace The Intel NVM Dimm Namespace to write data to @param[in] Offset bytes offset of the start of the data region to write @param[in] Length the length of the buffer to write @param[in] pBuffer Buffer with data to write @retval EFI_SUCCESS on a successful write @retval Error return values from IoNamespaceBlock function **/ EFI_STATUS WriteNamespaceBytes( IN NAMESPACE *pNamespace, IN CONST UINT64 Offset, IN VOID *pBuffer, IN CONST UINT64 Length ) { return IoNamespaceBytes(pNamespace, Offset, (CHAR8 *)pBuffer, (UINT32)Length, FALSE); } /** Performs a block read or write to the Namespace. The function calculates the proper DPA DIMM offset and issues the proper read or write operation on the destination DIMM. @param[in] pNamespace The Intel NVM Dimm Namespace to perform the IO Block operation. @param[in] Lba the Logica Block Addressing block offset to perform the IO on. @param[out] pBuffer the destination/source buffer where or from the data will be copied. @param[in] BlockLength the length of the buffer - should equal to the namespace block size. @param[in] ReadOperation boolean value indicating what type of IO is requested. TRUE means a read operation, and FALSE results in write operation. @retval EFI_SUCCESS if the IO operation was performed without errors. @retval Other return codes from functions: DimmRead, DimmWrite, AppDirectIo **/ EFI_STATUS IoNamespaceBlock( IN NAMESPACE *pNamespace, IN CONST UINT64 Lba, OUT CHAR8 *pBuffer, IN CONST UINT32 BlockLength, IN CONST BOOLEAN ReadOperation ) { EFI_STATUS ReturnCode = EFI_ABORTED; UINT64 Offset = 0; NVDIMM_ENTRY(); Offset = Lba * pNamespace->Media.BlockSize; ReturnCode = AppDirectIo(pNamespace, Offset, pBuffer, BlockLength, ReadOperation); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Performs a read or write to the AppDirect Namespace. The data is read/written from/to Interleave Set mapped in system memory. @param[in] pNamespace Intel NVM Dimm Namespace to perform the IO operation. @param[in] Offset Offset of AppDirect Namespace @param[in, out] pBuffer Destination/source buffer where or from the data will be copied. @param[in] Nbytes Number of bytes to read/write @param[in] ReadOperation boolean value indicating what type of IO is requested. @retval EFI_SUCCESS If the IO operation was performed without errors. @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS AppDirectIo( IN NAMESPACE *pNamespace, IN UINT64 Offset, IN OUT CHAR8 *pBuffer, IN UINT64 Nbytes, IN BOOLEAN ReadOperation ) { #ifndef OS_BUILD EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LIST_ENTRY *pNode = NULL; DIMM_REGION *pRegion = NULL; UINT8 *pSpaStart = NULL; UINT32 Index = 0; UINT8 *pAddress = 0; NVDIMM_ENTRY(); if (pNamespace == NULL || pNamespace->pParentIS == NULL || pBuffer == NULL) { goto Finish; } pSpaStart = (UINT8 *) pNamespace->SpaNamespaceBase; pAddress = pSpaStart + Offset; if (gArsBadRecordsCount != 0) { ReturnCode = IsAddressRangeInArsList((UINT64)pAddress, Nbytes); if (EFI_ERROR(ReturnCode)) { goto Finish; } } if (ReadOperation) { CopyMem(pBuffer, pAddress, Nbytes); } else { CopyMem(pAddress, pBuffer, Nbytes); for (Index = 0; Index < Nbytes / CACHE_LINE_SIZE; Index++) { gClFlush(pAddress + (Index * CACHE_LINE_SIZE)); } LIST_FOR_EACH(pNode, &pNamespace->pParentIS->DimmRegionList) { pRegion = DIMM_REGION_FROM_NODE(pNode); DimmWPQFlush(pRegion->pDimm); } AsmSfence(); } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; #else return EFI_UNSUPPORTED; #endif } /** Performs a buffer read or write to the namespace. The function calculates the proper DPA DIMM offset and issues the proper read or write operation on the destination DIMM. @param[in] pNamespace The Intel NVM Dimm Namespace to perform the IO buffer operation. @param[in] Offset - byte offset on the Namespace to perform the IO on - should be a multiple of cache line size. @param[out] pBuffer the destination/source buffer where or from the data will be copied. @param[in] BufferLength the length of the buffer - should be a multiple of cache line size. @param[in] ReadOperation boolean value indicating what type of IO is requested. TRUE means a read operation, and FALSE results in write operation. @retval EFI_SUCCESS if the IO operation was performed without errors. @retval EFI_INVALID_PARAMETER if pNamespace and/or pBuffer equals NULL. @retval EFI_BAD_BUFFER_SIZE if Offset and/or BufferLength are not aligned to the cache line size. @retval Other return codes from functions: DimmRead, DimmWrite, AppDirectIo **/ EFI_STATUS IoNamespaceBytes( IN NAMESPACE *pNamespace, IN UINT64 Offset, OUT CHAR8 *pBuffer, IN UINT32 BufferLength, IN BOOLEAN ReadOperation ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pNamespace == NULL || pBuffer == NULL) { goto Finish; } ReturnCode = AppDirectIo(pNamespace, Offset, pBuffer, BufferLength, ReadOperation); if (EFI_ERROR(ReturnCode)) { goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Read block from the namespace The function reads only one block from the device, the buffer length needs to be at least the Namespace block size. @param[in] pNamespace Namespace that data will be read from @param[in] Lba LBA to retrieve read from @param[out] pBuffer pointer to the memory where the result should be stored @retval EFI_SUCCESS on a successful read @retval Error return values from BttRead or ReadNamespaceBlock function **/ EFI_STATUS ReadBlockDevice( IN NAMESPACE *pNamespace, IN UINT64 Lba, OUT CHAR8 *pBuffer ) { if (pNamespace->IsBttEnabled) { if (pNamespace->pBtt == NULL) { return EFI_NOT_READY; } return BttRead(pNamespace->pBtt, Lba, pBuffer); } else if (pNamespace->IsPfnEnabled) { if (pNamespace->pPfn == NULL) { return EFI_NOT_READY; } return PfnRead(pNamespace->pPfn, Lba, pBuffer); } else if (pNamespace->IsRawNamespace) { return ReadNamespaceBlock(pNamespace, Lba, pBuffer); } else { return EFI_UNSUPPORTED; } } /** Write to the namespace through the BTT mechanism. The function writes only one block from the device, the buffer length needs to be at least the Namespace block size. @param[in] pNamespace Namespace containing the BTT @param[in] Lba LBA to store the write to @param[out] pBuffer pointer to the memory where the destination data resides @retval EFI_SUCCESS on a successful write @retval Error return values from BttWrite or WriteNamespaceBlock function **/ EFI_STATUS WriteBlockDevice( IN NAMESPACE *pNamespace, IN UINT64 Lba, OUT CHAR8 *pBuffer ) { if (pNamespace->IsBttEnabled) { if (pNamespace->pBtt == NULL) { return EFI_NOT_READY; } return BttWrite(pNamespace->pBtt, Lba, pBuffer); } else if (pNamespace->IsPfnEnabled) { if (pNamespace->pPfn == NULL) { return EFI_NOT_READY; } return PfnWrite(pNamespace->pPfn, Lba, pBuffer); } else if (pNamespace->IsRawNamespace) { return WriteNamespaceBlock(pNamespace, Lba, pBuffer); } else { return EFI_UNSUPPORTED; } } /** Compare region length field in MemoryMapRange Struct @param[in] pFirst First item to compare @param[in] pSecond Second item to compare @retval -1 if first is less than second @retval 0 if first is equal to second @retval 1 if first is greater than second **/ STATIC INT32 CompareRegionLengthInMemoryRange( IN VOID *pFirst, IN VOID *pSecond ) { MEMMAP_RANGE *pRange = NULL; MEMMAP_RANGE *pRange2 = NULL; if (pFirst == NULL || pSecond == NULL) { NVDIMM_DBG("NULL pointer found."); return 0; } pRange = MEMMAP_RANGE_FROM_NODE(pFirst); pRange2 = MEMMAP_RANGE_FROM_NODE(pSecond); if (pRange->RangeLength < pRange2->RangeLength) { return -1; } else if (pRange->RangeLength > pRange2->RangeLength) { return 1; } else { return 0; } } /** Compare region DPA Start field in MemoryMapRange Struct @param[in] pFirst First item to compare @param[in] pSecond Second item to compare @retval -1 if first is less than second @retval 0 if first is equal to second @retval 1 if first is greater than second **/ STATIC INT32 CompareRegionDpaStartInMemoryRange( IN VOID *pFirst, IN VOID *pSecond ) { MEMMAP_RANGE *pRange = NULL; MEMMAP_RANGE *pRange2 = NULL; if (pFirst == NULL || pSecond == NULL) { NVDIMM_DBG("NULL pointer found."); return 0; } pRange = MEMMAP_RANGE_FROM_NODE(pFirst); pRange2 = MEMMAP_RANGE_FROM_NODE(pSecond); if (pRange->RangeStartDpa < pRange2->RangeStartDpa) { return -1; } else if (pRange->RangeStartDpa > pRange2->RangeStartDpa) { return 1; } else { return 0; } } /** Find the Memory map range with the lowest DPA that contains a given region size in a given IS. IS that is passed in is of the type the user wishes to create NS on Size has already been aligned for NS requirements If size is MAX_UINT64 ignore lowest DPA and instead find the largest free memory region for that exists on all dimms in the interleave set @param[in] pIS pointer to Interleave Set that the memory range is contained in. @param[in] Size Minimum size the memory map range must be, if MAX_UINT64 then find the largest possible. @param[out] pFoundRange pointer memory range structure range will be copied into @retval EFI_SUCCESS everything went fine @retval EFI_OUT_OF_RESOURCES when memory allocation fails @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. @retval EFI_NOT_FOUND could not find a region of given size in IS Other return codes from functions: GetDimmFreemap GetListSize **/ EFI_STATUS FindADMemmapRangeInIS( IN NVM_IS *pIS, IN UINT64 Size, OUT MEMMAP_RANGE *pFoundRange ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LIST_ENTRY **ppFreemapList = NULL; LIST_ENTRY *pNode = NULL; LIST_ENTRY *pNode2 = NULL; LIST_ENTRY *pNode3 = NULL; DIMM_REGION *pDimmRegion = NULL; MEMMAP_RANGE *pRange = NULL; MEMMAP_RANGE *pRange2 = NULL; UINT64 ISStartDpa = 0; UINT64 ISEndDpa = 0; UINT32 DimmNum = 0; UINT32 Index = 0; BOOLEAN Found = FALSE; if (pIS == NULL || IsListEmpty(&pIS->DimmRegionList) || pFoundRange == NULL) { goto Finish; } ZeroMem(pFoundRange, sizeof(*pFoundRange)); ReturnCode = GetListSize(&pIS->DimmRegionList, &DimmNum); if (EFI_ERROR(ReturnCode)) { goto Finish; } ppFreemapList = (LIST_ENTRY **) AllocateZeroPool(sizeof(*ppFreemapList) * DimmNum); if (ppFreemapList == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } LIST_FOR_EACH(pNode, &pIS->DimmRegionList) { ppFreemapList[Index] = (LIST_ENTRY *) AllocateZeroPool(sizeof(*ppFreemapList[Index])); if (ppFreemapList[Index] == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } InitializeListHead(ppFreemapList[Index]); pDimmRegion = DIMM_REGION_FROM_NODE(pNode); ReturnCode = GetDimmFreemap(pDimmRegion->pDimm, FreeCapacityForADMode, ppFreemapList[Index]); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to get free memory range"); goto Finish; } if (IsListEmpty(ppFreemapList[Index])) { ReturnCode = EFI_NOT_FOUND; goto Finish; } Index++; } pNode = GetFirstNode(&pIS->DimmRegionList); pDimmRegion = DIMM_REGION_FROM_NODE(pNode); /** We can assume the IS on the first dimm starts at the same location on all dimms in the IS **/ ISStartDpa = pDimmRegion->pDimm->PmStart + pDimmRegion->PartitionOffset; ISEndDpa = ISStartDpa + pDimmRegion->PartitionSize; if (Size == MAX_UINT64_VALUE) { ReturnCode = BubbleSortLinkedList(ppFreemapList[0], CompareRegionLengthInMemoryRange); } else { ReturnCode = BubbleSortLinkedList(ppFreemapList[0], CompareRegionDpaStartInMemoryRange); } if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to bubble sort free memory ranges"); goto Finish; } if (Size == MAX_UINT64_VALUE) { LIST_FOR_EACH_REVERSE(pNode2, ppFreemapList[0]) { pRange = MEMMAP_RANGE_FROM_NODE(pNode2); /** We must find range that exists inside IS **/ if (pRange->RangeStartDpa >= ISStartDpa && ((pRange->RangeStartDpa + pRange->RangeLength) <= ISEndDpa)) { /** Free memory range candidate exists on first dimm, now validate the same candidate range we are requesting exists on other dimms. If candidate range is not discovered on a single dimm it is not a valid candidate range. **/ Found = TRUE; for (Index=1; Index < DimmNum; Index++) { Found = FALSE; LIST_FOR_EACH(pNode3, ppFreemapList[Index]) { pRange2 = MEMMAP_RANGE_FROM_NODE(pNode3); if (pRange2->RangeStartDpa >= ISStartDpa && pRange2->RangeStartDpa + pRange2->RangeLength <= ISEndDpa && /** Range exists in the InterleaveSet **/ pRange->RangeStartDpa >= pRange2->RangeStartDpa && /** Start of the candidate is not before the start of the test range **/ pRange->RangeStartDpa + pRange->RangeLength <= pRange2->RangeStartDpa + pRange2->RangeLength) { /**end of the candidate is before or at the end of the test range **/ Found = TRUE; break; } } /** Unable to find region on more than one dimm, invalid candidate range, try next candidate range **/ if (!Found) { break; } } /** Since candidate list is sorted by smallest range first and searched in reverse order, the first match will have the largest memory range **/ if (Found) { break; } } } } else { LIST_FOR_EACH(pNode2, ppFreemapList[0]) { pRange = MEMMAP_RANGE_FROM_NODE(pNode2); /** We must find range that exists inside IS && is larger than the requested capacity **/ if (pRange->RangeLength >= Size && pRange->RangeStartDpa >= ISStartDpa && (pRange->RangeStartDpa + pRange->RangeLength <= ISEndDpa)) { /** Free memory range candidate exists on first dimm, now validate the same candidate range we are requesting exists on other dimms. If candidate range is not discovered on a single dimm it is not a valid candidate range. **/ Found = TRUE; for (Index=1; Index < DimmNum; Index++) { Found = FALSE; LIST_FOR_EACH(pNode3, ppFreemapList[Index]) { pRange2 = MEMMAP_RANGE_FROM_NODE(pNode3); if (pRange2->RangeLength >= Size && /** Large enough for request **/ pRange2->RangeStartDpa >= ISStartDpa && pRange2->RangeStartDpa + pRange2->RangeLength <= ISEndDpa && /** Range exists in the InterleaveSet **/ pRange->RangeStartDpa >= pRange2->RangeStartDpa && /** Start of the candidate is not before the start of the test range **/ pRange->RangeStartDpa + pRange->RangeLength <= pRange2->RangeStartDpa + pRange2->RangeLength) { /**end of the candidate is before or at the end of the test range **/ Found = TRUE; break; } } /** Unable to find region on more than one dimm, invalid candidate range, try next candidate range **/ if (!Found) { break; } } /** Since candidate list is sorted by DPA, the first match will have the lowest DPA **/ if (Found) { break; } } } } if (!Found) { ReturnCode = EFI_NOT_FOUND; } else { CopyMem_S(pFoundRange, sizeof(*pFoundRange), pRange, sizeof(*pFoundRange)); } Finish: if (ppFreemapList != NULL) { for(Index = 0; Index < DimmNum; Index++) { FreeMemmapItems(ppFreemapList[Index]); FREE_POOL_SAFE(ppFreemapList[Index]); } FREE_POOL_SAFE(ppFreemapList); } return ReturnCode; } /** Find the Starting address of an Appdirect Namespace in the SPA @param[in] pNamespace pointer to Namespace to calculate the base SPA for @param[in] pIS pointer to Interleave Set that namespace is contained in. @retval EFI_SUCCESS everything went fine @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. **/ STATIC EFI_STATUS CalculateAppDirectNamespaceBaseSpa( IN NAMESPACE *pNamespace, IN NVM_IS *pIS ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT16 Index = 0; NvDimmRegionMappingStructure *pDimmRegionTable = NULL; NVDIMM_ENTRY(); if (pNamespace == NULL || pIS == NULL) { goto Finish; } for (Index = 0; Index < pNamespace->RangesCount; Index++) { ReturnCode = GetNvDimmRegionMappingStructureForPid( gNvmDimmData->PMEMDev.pFitHead, pNamespace->Range[Index].pDimm->DimmID, NULL, TRUE, pIS->pSpaTbl->SpaRangeDescriptionTableIndex, &pDimmRegionTable); if (EFI_ERROR(ReturnCode) || pDimmRegionTable == NULL) { NVDIMM_DBG("Unable to locate NvDimmRegion table in NFIT for dimm"); goto Finish; } // First Dimm in the interleave set if (pDimmRegionTable->RegionOffset == 0) { if (pNamespace->Range[Index].Dpa < pDimmRegionTable->NvDimmPhysicalAddressRegionBase) { NVDIMM_DBG("DPA exists before RegionBase"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pNamespace->SpaNamespaceBase = pIS->pSpaTbl->SystemPhysicalAddressRangeBase + ((pNamespace->Range[Index].Dpa - pDimmRegionTable->NvDimmPhysicalAddressRegionBase) * pNamespace->RangesCount); break; } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Namespace Capacity Alignment Aligning the Namespace Capacity Helper Function @param [in] NamespaceCapacity Namespace Capacity provided @param [in] DimmCount @param [out] *pAlignedNamespaceCapacity Total Namespace Capacity after alignment @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_SUCCESS Namespace capacity aligned **/ EFI_STATUS AlignNamespaceCapacity( IN UINT64 NamespaceCapacity, IN UINT64 DimmCount, OUT UINT64* pAlignedNamespaceCapacity ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT64 NamespaceCapacityPerDimm = 0; if (pAlignedNamespaceCapacity == NULL) { goto Finish; } if (NamespaceCapacity < GIB_TO_BYTES(32)) { NVDIMM_DBG("Capacity length is < 32 bit then it is at 4K Alignment "); NamespaceCapacityPerDimm = ROUNDUP((NamespaceCapacity/DimmCount), NAMESPACE_4KB_ALIGNMENT_SIZE); } else if (NamespaceCapacity < TIB_TO_BYTES(1)) { NVDIMM_DBG("Capacity length is < 40 bit then it is at 4K Alignment "); NamespaceCapacityPerDimm = ROUNDUP((NamespaceCapacity / DimmCount), NAMESPACE_4KB_ALIGNMENT_SIZE); } else if (NamespaceCapacity < TIB_TO_BYTES(256)) { NVDIMM_DBG("Capacity length is < 48 bit then it is at 64K Alignment "); NamespaceCapacityPerDimm = ROUNDUP((NamespaceCapacity / DimmCount), NAMESPACE_64KB_ALIGNMENT_SIZE); } else { NVDIMM_DBG("Capacity length is < 60 bit then it is at 32G Alignment "); NamespaceCapacityPerDimm = ROUNDUP((NamespaceCapacity / DimmCount), NAMESPACE_32GB_ALIGNMENT_SIZE); } *pAlignedNamespaceCapacity = NamespaceCapacityPerDimm * DimmCount; NVDIMM_DBG("AlignmentCapacity: %u \n", *pAlignedNamespaceCapacity); ReturnCode = EFI_SUCCESS; Finish: return ReturnCode; } /** Provision Namespace capacity on a DIMM or Interleave Set. Only one of target pointers must be provided, other one must be NULL. Function analyses possibility of allocation of NamespaceCapacity bytes on a DIMM or on an Interleave Set. If there is enough free space function assigns appropriate Namespace regions. @param[in] pDimm DIMM structure pointer in case of Block Mode Namespaces @param[in] pIS Interleave Set structure pointer in case of Persistent Memory Namespaces @param[in out] NamespaceCapacity Required capacity in bytes @param[out] pNamespace Namespace which regions will be updated if allocation is successful @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_OUT_OF_RESOURCES Not enough free space on target @retval EFI_SUCCESS Namespace capacity allocated **/ EFI_STATUS AllocateNamespaceCapacity( IN DIMM *pDimm, OPTIONAL IN NVM_IS *pIS, OPTIONAL IN OUT UINT64 *pNamespaceCapacity, OUT NAMESPACE *pNamespace ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LIST_ENTRY *pFreemapList = NULL; LIST_ENTRY *pNode = NULL; DIMM_REGION *pRegion = NULL; MEMMAP_RANGE AppDirectRange; UINT32 DimmCount = 0; UINT64 AlignedNamespaceCapacitySize = 0; UINT64 RegionSize = 0; UINT16 Index = 0; NVDIMM_ENTRY(); ZeroMem(&AppDirectRange, sizeof(AppDirectRange)); if ((pDimm == NULL && pIS == NULL) || (pDimm != NULL && pIS != NULL) || (pNamespaceCapacity == NULL) || (pIS == NULL && pNamespace->NamespaceType == APPDIRECT_NAMESPACE)) { goto Finish; } if (pNamespace->NamespaceType == APPDIRECT_NAMESPACE) { if (pIS->State != IS_STATE_HEALTHY) { NVDIMM_DBG("Interleave Set %d is not active", pIS->InterleaveSetIndex); ReturnCode = EFI_NOT_READY; goto Finish; } // Provision capacity equally on all DIMMs of the Interleave Set ReturnCode = GetListSize(&pIS->DimmRegionList, &DimmCount); if (EFI_ERROR(ReturnCode) || DimmCount == 0) { goto Finish; } ReturnCode = AlignNamespaceCapacity(*pNamespaceCapacity, DimmCount, &AlignedNamespaceCapacitySize); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Namespace Capacity is an invalid parameter"); ReturnCode = EFI_INVALID_PARAMETER; } if (*pNamespaceCapacity % DimmCount != 0) { NVDIMM_WARN("Unable to equally distribute required capacity among %d regions. " "Allocated Capacity will be increased to %lld", DimmCount, AlignedNamespaceCapacitySize); } RegionSize = AlignedNamespaceCapacitySize / DimmCount; ReturnCode = FindADMemmapRangeInIS(pIS, RegionSize, &AppDirectRange); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Did not find free area of requested size %llx", *pNamespaceCapacity); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } *pNamespaceCapacity = AlignedNamespaceCapacitySize; Index = 0; LIST_FOR_EACH(pNode, &pIS->DimmRegionList) { pRegion = DIMM_REGION_FROM_NODE(pNode); pNamespace->Range[Index].pDimm = pRegion->pDimm; pNamespace->Range[Index].Dpa = AppDirectRange.RangeStartDpa; pNamespace->Range[Index].Size = RegionSize; Index++; } pNamespace->RangesCount = Index; ReturnCode = CalculateAppDirectNamespaceBaseSpa(pNamespace, pIS); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to locate base SPA address of Namespace"); goto Finish; } } ReturnCode = EFI_SUCCESS; Finish: if (pFreemapList != NULL) { FreeMemmapItems(pFreemapList); FREE_POOL_SAFE(pFreemapList); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check for free slot status of Label Storage Area. Function analyses Label Storage Area and returns number of free slots and first slot number whichever is required. @param[in] pLsa Label Area Storage structure pointer @param[out] pFreeSlotsCount Number of free slots found @param[out] pFirstFreeSlotId First free slot number @retval EFI_INVALID_PARAMETER LSA pointer not provided @retval EFI_SUCCESS Free slots information determined correctly **/ #define UNDEFINED_SLOT_ID 0xFFFF EFI_STATUS GetLsaFreeSlots( IN LABEL_STORAGE_AREA *pLsa, OUT UINT16 *pFreeSlotsCount, OPTIONAL OUT UINT16 *pFirstFreeSlotId OPTIONAL ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT16 CurrentIndex = 0; UINT16 Index = 0; UINT16 SlotStatus = SLOT_UNKNOWN; NVDIMM_ENTRY(); if (pFreeSlotsCount != NULL) { *pFreeSlotsCount = 0; } if (pFirstFreeSlotId != NULL) { *pFirstFreeSlotId = UNDEFINED_SLOT_ID; } ReturnCode = GetLsaIndexes(pLsa, &CurrentIndex, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } for (Index = 0; Index < pLsa->Index[CurrentIndex].NumberOfLabels; Index++) { CheckSlotStatus(&pLsa->Index[CurrentIndex], Index, &SlotStatus); if (SlotStatus == SLOT_FREE) { if (pFreeSlotsCount != NULL) { *pFreeSlotsCount += 1; } if (pFirstFreeSlotId != NULL && *pFirstFreeSlotId == UNDEFINED_SLOT_ID) { *pFirstFreeSlotId = Index; NVDIMM_DBG("First free slot: %d", *pFirstFreeSlotId); } } } ReturnCode = EFI_SUCCESS; Finish: if (pFreeSlotsCount != NULL) { NVDIMM_DBG("Free slots count: %d", *pFreeSlotsCount); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Aligns the size of the Label Index area and calculates the number of free blocks, padding the driver can support. @param[in] PcdSize Size of the PCD Partition area @param[in] UseLatestLabelVersion Use latest version of Labels @param[out] pFreeBlocks The size of free array @param[out] pPadding The padding required to complete the index area @retval EFI_INVALID_PARAMETER null parameter provided @retval EFI_SUCCESS Alignment went well **/ EFI_STATUS AlignLabelStorageArea( IN UINT32 PcdSize, IN BOOLEAN UseLatestLabelVersion, OUT UINT32 *pFreeBlocks, OUT UINT32 *pPadding ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 TotalLabels = 0; UINT32 FreeOffset = 0; UINT32 IndexSize = MIN_NAMESPACE_LABEL_INDEX_SIZE; UINT32 LabelSize = 0; if ((pFreeBlocks == NULL) || (pPadding == NULL)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } LabelSize = (UseLatestLabelVersion) ? sizeof(NAMESPACE_LABEL) : sizeof(NAMESPACE_LABEL_1_1); FreeOffset = OFFSET_OF(NAMESPACE_INDEX, pFree); TotalLabels = PcdSize / LabelSize; *pFreeBlocks = LABELS_TO_FREE_BYTES(TotalLabels); if ((FreeOffset + *pFreeBlocks) > MIN_NAMESPACE_LABEL_INDEX_SIZE) { IndexSize = ROUNDUP(FreeOffset + *pFreeBlocks, NSINDEX_ALIGN); } *pPadding = IndexSize - (FreeOffset + *pFreeBlocks); Finish: return ReturnCode; } /** Gets the Label Index Data in a contiguous memory It is the caller's responsibility to free the memory. @param[in] pLsa The Label Storage Area @param[in] LabelIndex The index of the LSA @param[out] pRawData Pointer to the contiguous memory region @retval EFI_INVALID_PARAMETER NULL pointer provided @retval EFI_OUT_OF_RESOURCES could not allocate memory @retval EFI_SUCCESS Successfully transferred memory **/ EFI_STATUS LabelIndexAreaToRawData( IN LABEL_STORAGE_AREA *pLsa, IN UINT32 LabelIndex, OUT UINT8 **ppRawData ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT8 *pBuffer = NULL; UINT64 IndexSize = 0; UINT64 FreeOffset = 0; UINT64 NumFreeBytes = 0; UINT64 TotalIndexSize = 0; NAMESPACE_INDEX *pNamespaceIndex = NULL; UINT32 Index = 0; if (pLsa == NULL) { goto Finish; } Index = (LabelIndex == ALL_INDEX_BLOCKS) ? 0 : LabelIndex; pNamespaceIndex = &pLsa->Index[Index]; if (pNamespaceIndex == NULL) { goto Finish; } IndexSize = pNamespaceIndex->MySize; if (IndexSize == 0) { goto Finish; } TotalIndexSize = (LabelIndex == ALL_INDEX_BLOCKS) ? (ALL_INDEX_BLOCKS * IndexSize) : IndexSize; *ppRawData = AllocateZeroPool(TotalIndexSize); if (*ppRawData == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pBuffer = *ppRawData; FreeOffset = OFFSET_OF(NAMESPACE_INDEX, pFree); NumFreeBytes = LABELS_TO_FREE_BYTES(ROUNDUP(pNamespaceIndex->NumberOfLabels, 8)); for (; Index < ALL_INDEX_BLOCKS; Index++) { pNamespaceIndex = &pLsa->Index[Index]; if (pNamespaceIndex == NULL) { goto Finish; } CopyMem_S(pBuffer, TotalIndexSize, pNamespaceIndex, FreeOffset); if (TotalIndexSize < FreeOffset) { NVDIMM_ERR("TotalIndexSize is smaller than FreeOffset. Leads to negative destination buffer size."); ReturnCode = EFI_BAD_BUFFER_SIZE; goto Finish; } CopyMem_S(pBuffer + FreeOffset, TotalIndexSize - FreeOffset, pNamespaceIndex->pFree, NumFreeBytes); pBuffer += pNamespaceIndex->MySize; TotalIndexSize -= pNamespaceIndex->MySize; if (LabelIndex != ALL_INDEX_BLOCKS) { break; } } ReturnCode = EFI_SUCCESS; Finish: return ReturnCode; } /** Check if LSA of a specified DIMM is initialized. If empty LSA is detected then it is initialized. If non empty LSA is detected then it is validated for data correctness. @param[in] pDimm Target DIMM @param[in] LabelVersionMajor Major version of label to init @param[in] LabelVersionMinor Minor version of label to init @param[in] ForceInitialization If true, always create a new LSA @retval EFI_INVALID_PARAMETER NULL pointer provided @retval EFI_VOLUME_CORRUPTED LSA data is broken @retval EFI_SUCCESS Valid LSA detected or initialized correctly **/ EFI_STATUS InitializeLabelStorageArea( IN DIMM *pDimm, IN UINT16 LabelVersionMajor, IN UINT16 LabelVersionMinor, IN BOOLEAN ForceInitialization ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LABEL_STORAGE_AREA *pLsa = NULL; NAMESPACE_INDEX *pNewIndex = NULL; UINT128 IndexSignature; UINT16 Index = 0; BOOLEAN ChecksumInserted = FALSE; UINT32 FreeBlocks = 0; UINT32 Padding = 0; UINT8 *pRawData = NULL; UINT32 ChecksumOffset = 0; UINT32 LabelSize = 0; BOOLEAN UseLatestLabelVersion = FALSE; NVDIMM_ENTRY(); SetMem(&IndexSignature, sizeof(IndexSignature), 0x0); if (pDimm == NULL) { goto Finish; } #ifndef OS_BUILD if (!ForceInitialization) { ReturnCode = ReadLabelStorageArea(pDimm->DimmID, &pLsa); if (ReturnCode != EFI_NOT_FOUND) { // Here we have a validated LSA or a corrupted LSA, just pass the result up goto Finish; } } #endif // OS_BUILD /** If ReadLabelStorageArea fails to validate the DIMM LSA, it frees the buffer and assigns NULL to it. Then we need to allocate it here to initialize. **/ if (pLsa == NULL) { pLsa = AllocateZeroPool(sizeof(*pLsa)); if (pLsa == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } } // Proceed with initialization NVDIMM_DBG("Initializing LSA on DIMM: 0x%x", pDimm->DeviceHandle.AsUint32); IndexSignature.Uint64 = LSA_NAMESPACE_INDEX_SIG_L; IndexSignature.Uint64_1 = LSA_NAMESPACE_INDEX_SIG_H; if ((LabelVersionMajor == NSINDEX_MAJOR) && (LabelVersionMinor == NSINDEX_MINOR_2)) { UseLatestLabelVersion = TRUE; } AlignLabelStorageArea(pDimm->PcdLsaPartitionSize, UseLatestLabelVersion, &FreeBlocks, &Padding); if ((FreeBlocks == 0) && (Padding == 0)) { goto Finish; } ChecksumOffset = OFFSET_OF(NAMESPACE_INDEX, Checksum); LabelSize = (UseLatestLabelVersion) ? sizeof(NAMESPACE_LABEL) : sizeof(NAMESPACE_LABEL_1_1); // Some fields are the same for both Indexes for (Index = 0; Index < NAMESPACE_INDEXES; Index++) { pNewIndex = &pLsa->Index[Index]; pNewIndex->MySize = OFFSET_OF(NAMESPACE_INDEX, pFree) + FreeBlocks + Padding; pNewIndex->LabelOffset = 2 * pNewIndex->MySize; pNewIndex->NumberOfLabels = FREE_BLOCKS_TO_LABELS(FreeBlocks) - ((UINT32)(pNewIndex->MySize / LabelSize) * 2); pNewIndex->Major = LabelVersionMajor; pNewIndex->Minor = LabelVersionMinor; pNewIndex->pFree = (UINT8 *) AllocateZeroPool( FreeBlocks); if (pNewIndex->pFree == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } SetMem(pNewIndex->pFree, FreeBlocks, 0xFF); CopyMem_S(&pNewIndex->Signature, sizeof(pNewIndex->Signature), &IndexSignature, NSINDEX_SIG_LEN); pNewIndex->LabelSize = (UINT8) BYTE_TO_INDEX_LABEL_SIZE(LabelSize); NVDIMM_DBG("LSA Index info %d: Index size: %d :: No of labels: %d", Index, pNewIndex->MySize, pNewIndex->NumberOfLabels); } // Rest of fields need to be initialized individually pLsa->Index[FIRST_INDEX_BLOCK].Sequence = 1; pLsa->Index[FIRST_INDEX_BLOCK].MyOffset = 0; pLsa->Index[FIRST_INDEX_BLOCK].OtherOffset = pLsa->Index[FIRST_INDEX_BLOCK].MySize; // Gets the Label Index data into a contiguous memory region ReturnCode = LabelIndexAreaToRawData(pLsa, FIRST_INDEX_BLOCK, &pRawData); if (EFI_ERROR(ReturnCode) || (pRawData == NULL)) { NVDIMM_DBG("Failed to convert label area index to raw data"); FREE_POOL_SAFE(pRawData); goto Finish; } ChecksumInserted = ChecksumOperations(pRawData, pLsa->Index[FIRST_INDEX_BLOCK].MySize, (UINT64 *)(pRawData + ChecksumOffset), TRUE); if (!ChecksumInserted) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pLsa->Index[FIRST_INDEX_BLOCK].Checksum = *(UINT64 *)(pRawData + ChecksumOffset); FREE_POOL_SAFE(pRawData); pLsa->Index[SECOND_INDEX_BLOCK].Sequence = 2; pLsa->Index[SECOND_INDEX_BLOCK].MyOffset = pLsa->Index[SECOND_INDEX_BLOCK].MySize; pLsa->Index[SECOND_INDEX_BLOCK].OtherOffset = 0; ReturnCode = LabelIndexAreaToRawData(pLsa, SECOND_INDEX_BLOCK, &pRawData); if (EFI_ERROR(ReturnCode) || (pRawData == NULL)) { NVDIMM_DBG("Failed to convert label area index to raw data"); FREE_POOL_SAFE(pRawData); goto Finish; } ChecksumInserted = ChecksumOperations(pRawData, pLsa->Index[SECOND_INDEX_BLOCK].MySize, (UINT64 *)(pRawData + ChecksumOffset), TRUE); if (!ChecksumInserted) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pLsa->Index[SECOND_INDEX_BLOCK].Checksum = *(UINT64 *)(pRawData + ChecksumOffset); FREE_POOL_SAFE(pRawData); pLsa->pLabels = AllocateZeroPool(sizeof(*(pLsa->pLabels)) * (pLsa->Index[FIRST_INDEX_BLOCK].NumberOfLabels)); if (pLsa->pLabels == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = WriteLabelStorageArea(pDimm->DimmID, pLsa); if (EFI_ERROR(ReturnCode)) { goto Finish; } Finish: if (!ChecksumInserted) { NVDIMM_DBG("Could not calculate the checksum."); } NVDIMM_EXIT_I64(ReturnCode); FreeLsaSafe(&pLsa); return ReturnCode; } /** Clear and initialize all label storage areas @param[in] ppDimms Array of DIMMs @param[in] DimmsNum Number of DIMMs @param[in] LabelVersionMajor Major version of label to init @param[in] LabelVersionMinor Minor version of label to init @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pDimmList is NULL @retval return codes from SendConfigInputToDimm **/ EFI_STATUS ClearAndInitializeAllLabelStorageAreas( IN DIMM **ppDimms, IN UINT32 DimmsNum, IN UINT16 LabelVersionMajor, IN UINT16 LabelVersionMinor, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LABEL_STORAGE_AREA *pLsa = NULL; UINT32 Index = 0; NVDIMM_ENTRY(); if (ppDimms == NULL || pCommandStatus == NULL) { return EFI_INVALID_PARAMETER; } for (Index = 0; Index < DimmsNum; Index++) { if (!IsDimmManageable(ppDimms[Index])) { continue; } ReturnCode = InitializeLabelStorageArea(ppDimms[Index], LabelVersionMajor, LabelVersionMinor, TRUE); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to initialize LSA data on a DIMM 0x%x", ppDimms[Index]->DeviceHandle.AsUint32); ppDimms[Index]->LsaStatus = LSA_COULD_NOT_INIT; SetObjStatusForDimm(pCommandStatus, ppDimms[Index], NVM_ERR_FAILED_TO_INIT_NS_LABELS); goto Finish; } // Check if the LSA was written properly and is not corrupted ReturnCode = ReadLabelStorageArea(ppDimms[Index]->DimmID, &pLsa); if (EFI_ERROR(ReturnCode)) { ppDimms[Index]->LsaStatus = LSA_CORRUPTED_AFTER_INIT; NVDIMM_DBG("LSA corrupted after initialization on DIMM 0x%x", ppDimms[Index]->DeviceHandle.AsUint32); SetObjStatusForDimm(pCommandStatus, ppDimms[Index], NVM_ERR_FAILED_TO_INIT_NS_LABELS); goto Finish; } FreeLsaSafe(&pLsa); ppDimms[Index]->LsaStatus = LSA_OK; SetObjStatusForDimm(pCommandStatus, ppDimms[Index], NVM_SUCCESS); } SetCmdStatus(pCommandStatus, NVM_SUCCESS); Finish: FreeLsaSafe(&pLsa); return ReturnCode; } /** Check if the label index version on the DIMM is v1.2 @param[in] pDimm The target DIMM @param[out] pHasLatestIndexVersion Pointer to boolean if the index is v1.2 @retval EFI_INVALID_PARAMETER Null parameter is passed @retval EFI_NOT_FOUND If no index blocks found on the DIMM @retval EFI_SUCCESS If found the label version successfully **/ EFI_STATUS CheckDimmNsLabelVersion( IN DIMM *pDimm, OUT BOOLEAN *pHasLatestIndexVersion ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT16 CurrentIndex = 0; BOOLEAN HasLatestVersion = FALSE; LABEL_STORAGE_AREA *pLsa = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || pHasLatestIndexVersion == NULL) { goto Finish; } if (pDimm->LsaStatus == LSA_NOT_INIT) { ReturnCode = EFI_NOT_FOUND; goto Finish; } else { ReturnCode = ReadLabelStorageArea(pDimm->DimmID, &pLsa); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = GetLsaIndexes(pLsa, &CurrentIndex, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } if ((pLsa->Index[CurrentIndex].Major == NSINDEX_MAJOR) && (pLsa->Index[CurrentIndex].Minor == NSINDEX_MINOR_2)) { HasLatestVersion = TRUE; } FreeLsaSafe(&pLsa); } *pHasLatestIndexVersion = HasLatestVersion; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); FreeLsaSafe(&pLsa); return ReturnCode; } /** Check if we can use the latest index block version @param[in] pNamespace The target namespace @param[in out] pUseLatestVersion Returns if the LSA uses new version TRUE if all DIMMs in the Namespace have same Major/Minor version of index blocks. No index blocks is treated as version 1.2 FALSE if the DIMMs in the Namespace have a mismatch of the Major/Minor versions in the index block @retval EFI_INVALID_PARAMETER Null parameter is passed @retval EFI_DEVICE_ERROR If the versions don't match on the DIMMs @retval EFI_OUT_OF_RESOURCES Memory allocation failed @retval EFI_SUCCESS Retrieved versions successfully **/ EFI_STATUS UseLatestNsLabelVersion( IN NVM_IS *pIS, IN DIMM *pParentDimm, OUT BOOLEAN *pUseLatestVersion ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LIST_ENTRY *pNode = NULL; DIMM_REGION *pDimmRegion = NULL; UINT16 Index = 0; BOOLEAN DimmLabelIsLatest = FALSE; BOOLEAN FirstDimmLabelIsLatest = FALSE; DIMM *pDimm = NULL; NVDIMM_ENTRY(); if (pUseLatestVersion == NULL) { goto Finish; } if (pParentDimm != NULL) { // Storage namespace pDimm = pParentDimm; ReturnCode = CheckDimmNsLabelVersion(pDimm, &DimmLabelIsLatest); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_NOT_FOUND) { // If no index block found, use v1.2 DimmLabelIsLatest = TRUE; } else { goto Finish; } } } else { // Appdirect namespace if (pIS == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } LIST_FOR_EACH(pNode, &pIS->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pNode); pDimm = pDimmRegion->pDimm; ReturnCode = CheckDimmNsLabelVersion(pDimm, &DimmLabelIsLatest); if (ReturnCode == EFI_NOT_FOUND) { // Did not find any index block on the DIMM, no issues, check next DIMM continue; } if (EFI_ERROR(ReturnCode)) { goto Finish; } if (Index == 0) { // Store info of first DIMM FirstDimmLabelIsLatest = DimmLabelIsLatest; } if (Index != 0 && FirstDimmLabelIsLatest != DimmLabelIsLatest) { /** Mismatch for index block versions on the DIMMs **/ ReturnCode = EFI_DEVICE_ERROR; goto Finish; } Index++; } // If none of the DIMMs in the IS had an index block, we will use v1.2 if (Index == 0) { DimmLabelIsLatest = TRUE; } } *pUseLatestVersion = DimmLabelIsLatest; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Creates the namespace labels based on the version of the labels @param[in] pNamespace The target namespace @param[in] Index The index of the label in the namespace @param[in] UseLatestVersion Use latest version of labels @param[in out] pLabel The output label @retval EFI_INVALID_PARAMETER Null parameter passed @retval EFI_SUCCESS Created new label successfully **/ EFI_STATUS CreateNamespaceLabels( IN NAMESPACE *pNamespace, IN UINT16 Index, IN BOOLEAN UseLatestVersion, IN OUT NAMESPACE_LABEL *pLabel ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; BOOLEAN ChecksumInserted = FALSE; NVM_IS *pIS = NULL; NVDIMM_ENTRY(); if (pNamespace == NULL || pLabel == NULL) { goto Finish; } pLabel->Dpa = pNamespace->Range[Index].Dpa; pLabel->RawSize = pNamespace->Range[Index].Size; pLabel->Flags = pNamespace->Flags; CopyMem_S(&pLabel->Uuid, sizeof(pLabel->Uuid), pNamespace->NamespaceGuid, sizeof(pLabel->Uuid)); CopyMem_S(&pLabel->Name, sizeof(pLabel->Name), pNamespace->Name, sizeof(pLabel->Name)); pIS = pNamespace->pParentIS; if (pIS == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pLabel->Position = (UINT16)Index; pLabel->NumberOfLabels = (UINT16)pNamespace->RangesCount; pLabel->InterleaveSetCookie = (UseLatestVersion) ? pIS->InterleaveSetCookie : pIS->InterleaveSetCookie_1_1; pLabel->LbaSize = (UseLatestVersion) ? AD_NAMESPACE_LABEL_LBA_SIZE_4K : 0; if (UseLatestVersion) { CopyGuid(&pLabel->TypeGuid, &gSpaRangePmRegionGuid); } if (UseLatestVersion) { if (pNamespace->IsBttEnabled) { CopyGuid(&pLabel->AddressAbstractionGuid, &gBttAbstractionGuid); } ChecksumInserted = ChecksumOperations(pLabel, sizeof(*pLabel), &pLabel->Checksum, TRUE); if (!ChecksumInserted) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Insert Namespace labels into a Label Storage Area of a DIMM. Function updates all required fields, index bitmap, sequence numbers, etc. Finally Label Storage Area is written to the DIMM. @param[in] pDimm DIMM structure pointer to which insert the labels @param[in] ppLabel Array of label structures to be inserted @param[in] LabelCount Number of labels in the array @param[in] LabelVersionMajor Major version of label to init @param[in] LabelVersionMinor Minor version of label to init @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_OUT_OF_RESOURCES No more label free slots in LSA @retval EFI_SUCCESS Operation successful **/ EFI_STATUS InsertNamespaceLabels( IN DIMM *pDimm, IN NAMESPACE_LABEL **ppLabel, IN UINT16 LabelCount, IN UINT16 LabelVersionMajor, IN UINT16 LabelVersionMinor ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LABEL_STORAGE_AREA *pLsa = NULL; UINT16 Index = 0; UINT16 SlotNumber = 0; UINT16 FreeSlots = 0; UINT16 CurrentIndex = 0; UINT16 NextIndex = 0; UINT32 FreeBlocks = 0; NVDIMM_ENTRY(); if (pDimm == NULL || ppLabel == NULL || LabelCount > MAX_NAMESPACE_RANGES) { goto Finish; } if (pDimm->LsaStatus == LSA_NOT_INIT) { ReturnCode = InitializeLabelStorageArea(pDimm, LabelVersionMajor, LabelVersionMinor, FALSE); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to initialize LSA data on a DIMM 0x%x", pDimm->DeviceHandle.AsUint32); pDimm->LsaStatus = LSA_COULD_NOT_INIT; goto Finish; } // Check if the LSA was written properly and is not corrupted ReturnCode = ReadLabelStorageArea(pDimm->DimmID, &pLsa); if (EFI_ERROR(ReturnCode)) { pDimm->LsaStatus = LSA_CORRUPTED_AFTER_INIT; NVDIMM_DBG("LSA corrupted after initialization on DIMM 0x%x", pDimm->DeviceHandle.AsUint32); goto Finish; } FreeLsaSafe(&pLsa); pDimm->LsaStatus = LSA_OK; } ReturnCode = ReadLabelStorageArea(pDimm->DimmID, &pLsa); if (EFI_ERROR(ReturnCode) || pLsa == NULL) { goto Finish; } GetLsaFreeSlots(pLsa, &FreeSlots, NULL); if (FreeSlots < LabelCount + 1) { // After adding the Labels at least one free slot must remain NVDIMM_DBG("Too few empty label slots."); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = GetLsaIndexes(pLsa, &CurrentIndex, &NextIndex); if (EFI_ERROR(ReturnCode)) { goto Finish; } FreeBlocks = LABELS_TO_FREE_BYTES(ROUNDUP(pLsa->Index[CurrentIndex].NumberOfLabels, 8)); // Copy free bitmap to next index before updating it CopyMem_S(pLsa->Index[NextIndex].pFree, FreeBlocks, pLsa->Index[CurrentIndex].pFree, FreeBlocks); // Insert labels & update next index free map for (Index = 0; Index < LabelCount; Index++) { ReturnCode = GetLsaFreeSlots(pLsa, NULL, &SlotNumber); if (EFI_ERROR(ReturnCode)) { goto Finish; } ppLabel[Index]->Slot = SlotNumber; CopyMem_S(&pLsa->pLabels[SlotNumber], sizeof(pLsa->pLabels[SlotNumber]), ppLabel[Index], sizeof(pLsa->pLabels[SlotNumber])); ChangeSlotStatus(&pLsa->Index[NextIndex], SlotNumber, SLOT_USED); } ReturnCode = UpdateLsaIndex(pLsa); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = WriteLabelStorageArea(pDimm->DimmID, pLsa); if (EFI_ERROR(ReturnCode)) { goto Finish; } Finish: FreeLsaSafe(&pLsa); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Modify attributes of a Label located at specified slot. Function updates all required fields, index bitmap, sequence numbers, etc. Finally Label Storage Area is written to the DIMM. @param[in] pDimm DIMM structure pointer to which insert the labels @param[in] SlotNumber Target LSA slot number @param[in] pFlags New value of Flags field OPTIONAL @param[in] pName New value of name OPTIONAL @param[in] TotalLabelsNum the new amount of the namespace labels OPTIONAL @param[in] pNewPosition pointer the new index position for this label OPTIONAL @param[in] NewRawSize new size for the label (support for modify) OPTIONAL @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_SUCCESS Operation successful **/ EFI_STATUS ModifyLabelAtSlot( IN DIMM *pDimm, IN UINT16 SlotNumber, IN UINT32 *pFlags, OPTIONAL IN CHAR8 *pName, OPTIONAL IN UINT16 TotalLabelsNum, OPTIONAL IN UINT16 *pNewPosition, OPTIONAL IN UINT64 NewRawSize OPTIONAL ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LABEL_STORAGE_AREA *pLsa = NULL; UINT16 NewSlot = UNDEFINED_SLOT_ID; UINT16 FreeSlots = 0; UINT16 CurrentIndex = 0; UINT16 NextIndex = 0; BOOLEAN ChecksumInserted = FALSE; UINT32 FreeBlocks = 0; BOOLEAN UseLatestVersion = FALSE; if (pDimm == NULL) { goto Finish; } ReturnCode = ReadLabelStorageArea(pDimm->DimmID, &pLsa); if (EFI_ERROR(ReturnCode) || pLsa == NULL) { goto Finish; } GetLsaFreeSlots(pLsa, &FreeSlots, &NewSlot); if (FreeSlots < 1) { // Label modification requires at least one free slot NVDIMM_DBG("Too few empty label slots."); goto Finish; } ReturnCode = GetLsaIndexes(pLsa, &CurrentIndex, &NextIndex); if (EFI_ERROR(ReturnCode)) { goto Finish; } if ((pLsa->Index[CurrentIndex].Major == NSINDEX_MAJOR) && (pLsa->Index[CurrentIndex].Minor == NSINDEX_MINOR_2)) { UseLatestVersion = TRUE; } FreeBlocks = LABELS_TO_FREE_BYTES(ROUNDUP(pLsa->Index[CurrentIndex].NumberOfLabels, 8)); // Copy free bitmap to next index before updating it CopyMem_S(pLsa->Index[NextIndex].pFree, FreeBlocks, pLsa->Index[CurrentIndex].pFree, FreeBlocks); // Copy Label to a new position CopyMem_S(&pLsa->pLabels[NewSlot], sizeof(pLsa->pLabels[NewSlot]), &pLsa->pLabels[SlotNumber], sizeof(pLsa->pLabels[NewSlot])); pLsa->pLabels[NewSlot].Slot = NewSlot; // Update Label fields if (pFlags != NULL) { pLsa->pLabels[NewSlot].Flags.AsUint32 = *pFlags; } if (pName != NULL) { CopyMem_S(&pLsa->pLabels[NewSlot].Name, sizeof(pLsa->pLabels[NewSlot].Name), pName, NSLABEL_NAME_LEN); } if (TotalLabelsNum != 0) { pLsa->pLabels[NewSlot].NumberOfLabels = TotalLabelsNum; } if (pNewPosition != NULL) { pLsa->pLabels[NewSlot].Position = *pNewPosition; } if (NewRawSize != 0) { pLsa->pLabels[NewSlot].RawSize = NewRawSize; } if (UseLatestVersion) { ChecksumInserted = ChecksumOperations(&pLsa->pLabels[NewSlot], sizeof(pLsa->pLabels[NewSlot]), &pLsa->pLabels[NewSlot].Checksum, TRUE); if (!ChecksumInserted) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } ChangeSlotStatus(&pLsa->Index[NextIndex], NewSlot, SLOT_USED); ChangeSlotStatus(&pLsa->Index[NextIndex], SlotNumber, SLOT_FREE); ZeroMem(&pLsa->pLabels[SlotNumber], sizeof(pLsa->pLabels[SlotNumber])); ReturnCode = UpdateLsaIndex(pLsa); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = WriteLabelStorageArea(pDimm->DimmID, pLsa); if (EFI_ERROR(ReturnCode)) { goto Finish; } Finish: FreeLsaSafe(&pLsa); return ReturnCode; } /** Modify attributes of all Namespace labels identified by UUID on a DIMM. Function updates all required fields, index bitmap, sequence numbers, etc. Finally Label Storage Area is written to the DIMM. @param[in] pDimm DIMM structure pointer to which insert the labels @param[in] pUuid Target Namespace identifier @param[in] pFlags New value of Flags field @param[in] pName New value of name @param[in] LabelsNum the new value for the total labels in the block namespace labels set @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_ABORTED Failed to modify the labels @retval EFI_SUCCESS Operation successful **/ EFI_STATUS ModifyNamespaceLabels( IN DIMM *pDimm, IN GUID *pUuid, IN UINT32 *pFlags, OPTIONAL IN CHAR8 *pName, OPTIONAL IN UINT16 LabelsNum OPTIONAL ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LABEL_STORAGE_AREA *pLsa = NULL; UINT16 Index = 0; UINT16 FreeSlots = 0; UINT16 FirstIndex = 0; UINT16 SlotsToModify[MAX_NAMESPACE_RANGES]; UINT16 SlotsToModifyCount = 0; ZeroMem(SlotsToModify, sizeof(SlotsToModify)); if (pDimm == NULL || pUuid == NULL) { goto Finish; } ReturnCode = ReadLabelStorageArea(pDimm->DimmID, &pLsa); if (EFI_ERROR(ReturnCode) || pLsa == NULL) { goto Finish; } GetLsaFreeSlots(pLsa, &FreeSlots, NULL); if (FreeSlots < 1) { // Label modification requires at least one free slot NVDIMM_DBG("Too few empty label slots."); goto Finish; } // Collect slot numbers with specified UUID first for (Index = 0; Index < pLsa->Index[FirstIndex].NumberOfLabels; Index++) { if (CompareMem(&pLsa->pLabels[Index].Uuid, pUuid, sizeof(GUID)) == 0) { NVDIMM_DBG("Slot %d marked for update", Index); SlotsToModify[SlotsToModifyCount] = Index; SlotsToModifyCount++; } } // Now proceed with update one by one for (Index = 0; Index < SlotsToModifyCount; Index++) { ReturnCode = ModifyLabelAtSlot(pDimm, SlotsToModify[Index], pFlags, pName, LabelsNum, NULL, 0); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_ABORTED; goto Finish; } } NVDIMM_DBG("Modified %d labels", SlotsToModifyCount); Finish: FreeLsaSafe(&pLsa); return ReturnCode; } /** Remove all Namespace labels with specified UUID from a DIMM. Function updates all required fields, index bitmap, sequence numbers, etc. Finally Label Storage Area is written to the DIMM. @param[in] pDimm DIMM structure pointer to which insert the labels @param[in] pUuid Target Namespace identifier @param[in] Dpa Limit removal to only one label corresponding to this DPA @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_SUCCESS Operation successful **/ EFI_STATUS RemoveNamespaceLabels( IN DIMM *pDimm, IN GUID *pUuid, IN UINT64 Dpa OPTIONAL ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LABEL_STORAGE_AREA *pLsa = NULL; UINT16 Index = 0; UINT16 CurrentIndex = 0; UINT16 NextIndex = 0; NAMESPACE_LABEL *pLabel = NULL; UINT32 FreeBlocks = 0; NVDIMM_ENTRY(); if (pDimm == NULL) { goto Finish; } ReturnCode = ReadLabelStorageArea(pDimm->DimmID, &pLsa); if (EFI_ERROR(ReturnCode) || pLsa == NULL) { goto Finish; } ReturnCode = GetLsaIndexes(pLsa, &CurrentIndex, &NextIndex); if (EFI_ERROR(ReturnCode)) { goto Finish; } FreeBlocks = LABELS_TO_FREE_BYTES(ROUNDUP(pLsa->Index[CurrentIndex].NumberOfLabels, 8)); // Copy free bitmap to next index before modifying it CopyMem_S(pLsa->Index[NextIndex].pFree, FreeBlocks, pLsa->Index[CurrentIndex].pFree, FreeBlocks); for (Index = 0; Index < pLsa->Index[NextIndex].NumberOfLabels; Index++) { pLabel = &pLsa->pLabels[Index]; if (CompareMem(&pLabel->Uuid, pUuid, sizeof(GUID)) != 0) { continue; } if (Dpa != 0 && Dpa != pLabel->Dpa) { continue; } ReturnCode = ChangeSlotStatus(&pLsa->Index[NextIndex], Index, SLOT_FREE); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to change slot %d status to %d", Index, SLOT_FREE); goto Finish; } ZeroMem(pLabel, sizeof(*pLabel)); NVDIMM_DBG("Removing label from slot %d", Index); } ReturnCode = UpdateLsaIndex(pLsa); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = WriteLabelStorageArea(pDimm->DimmID, pLsa); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = EFI_SUCCESS; Finish: FreeLsaSafe(&pLsa); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** This function calculates the real block and raw size that needs to be allocated on the DIMM in order to allow the caller of his desired block size and raw size. The function checks the Raw size to block size alignment and the block size to cache line size alignment, doing appropriate increasing or decreasing of the Raw Size. At least one of the optional parameters is required to be specified. One of: BlockCount or DesiredRawSize must be specified, the other needs to equal 0. @param[in] DesiredRawSize the raw size of the device (should be multiply of DesiredBlockSize, but if not, the function will decrease it accordingly). @param[in] DesiredBlockSize the required block size that will be aligned to the cache line size. @param[in] BlockCount the input block amount - specified by the caller. @param[out] pRealDimmRawSize pointer to where the real DIMM raw size needed will be stored. @param[out] pRealDimmBlockSize pointer to where the real DIMM block size will be stored. @param[out] pBlockCount pointer to where the calculated block count will be stored. @retval EFI_SUCCESS the parameters are OK and the values were calculated properly. @retval EFI_INVALID_PARAMETER the provided parameters are not passed accordingly to the function header. **/ EFI_STATUS GetRealRawSizeAndRealBlockSize( IN UINT64 DesiredRawSize OPTIONAL, IN UINT32 DesiredBlockSize, IN UINT64 BlockCount OPTIONAL, OUT UINT64 *pRealDimmRawSize OPTIONAL, OUT UINT32 *pRealDimmBlockSize OPTIONAL, OUT UINT64 *pBlockCount OPTIONAL ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 TempRealBlockSize = 0; if ((pRealDimmRawSize == NULL && pRealDimmBlockSize == NULL && pBlockCount == NULL) || (((DesiredRawSize != 0 && BlockCount != 0) || (DesiredRawSize == 0 && BlockCount == 0)) && DesiredBlockSize == 0) || (DesiredBlockSize == 0)) { goto Finish; } // If the desired size is not aligned to the desired block size - reduce the size until it is aligned. if (DesiredRawSize != 0) { // Slightly decrease the Raw size if it is not aligned with the desired block size. DesiredRawSize -= (DesiredRawSize % DesiredBlockSize); BlockCount = DesiredRawSize / DesiredBlockSize; } else { DesiredRawSize = BlockCount * DesiredBlockSize; } // If the block size is aligned to our Cache line size, it will stay the same. TempRealBlockSize = (UINT32) GetPhysicalBlockSize(DesiredBlockSize); if (pRealDimmRawSize != NULL) { *pRealDimmRawSize = BlockCount * TempRealBlockSize; } if (pRealDimmBlockSize != NULL) { *pRealDimmBlockSize = TempRealBlockSize; } if (pBlockCount != NULL) { *pBlockCount = BlockCount; } ReturnCode = EFI_SUCCESS; Finish: return ReturnCode; } /** Function updates sequence number and checksum of a next Index Block of the specified Label Storage Area. @param[in] pLsa Target Label Storage Area structure @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_OUT_OF_RESOURCES Unsuccessful checksum calculation @retval EFI_SUCCESS Operation successful **/ EFI_STATUS UpdateLsaIndex( IN LABEL_STORAGE_AREA *pLsa ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT16 CurrentIndex = 0; UINT16 NextIndex = 0; BOOLEAN ChecksumInserted = FALSE; UINT8 *pRawData = NULL; UINT32 ChecksumOffset = 0; NVDIMM_ENTRY(); ReturnCode = GetLsaIndexes(pLsa, &CurrentIndex, &NextIndex); if (EFI_ERROR(ReturnCode)) { goto Finish; } ChecksumOffset = OFFSET_OF(NAMESPACE_INDEX, Checksum); // Update sequence number and checksum pLsa->Index[NextIndex].Sequence = pLsa->Index[CurrentIndex].Sequence % 3 + 1; ReturnCode = LabelIndexAreaToRawData(pLsa, NextIndex, &pRawData); if (EFI_ERROR(ReturnCode) || (pRawData == NULL)) { NVDIMM_DBG("Failed to convert label area index to raw data"); goto Finish; } ChecksumInserted = ChecksumOperations(pRawData, pLsa->Index[CurrentIndex].MySize, (UINT64 *)(pRawData + ChecksumOffset), TRUE); pLsa->Index[NextIndex].Checksum = *(UINT64 *)(pRawData + ChecksumOffset); if (!ChecksumInserted) { NVDIMM_DBG("Could not insert the checksum after LSA update."); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pRawData); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Clean the namespaces list @param[in,out] pList: the list to clean **/ VOID CleanNamespacesList( IN OUT LIST_ENTRY *pList ) { NAMESPACE *pNamespace = NULL; LIST_ENTRY *pNamespaceNode = NULL; LIST_ENTRY *pNamespaceNext = NULL; NVDIMM_ENTRY(); if (pList == NULL) { goto Finish; } /** Free namespaces and remove them from the namespaces list **/ LIST_FOR_EACH_SAFE(pNamespaceNode, pNamespaceNext, pList) { pNamespace = NAMESPACE_FROM_NODE(pNamespaceNode, NamespaceNode); RemoveEntryList(pNamespaceNode); FREE_POOL_SAFE(pNamespace); } Finish: NVDIMM_EXIT(); } /** Compare region SPA offset field in interleave set cookie structure @param[in] pFirst First item to compare @param[in] pSecond Second item to compare @retval -1 if first is less than second @retval 0 if first is equal to second @retval 1 if first is greater than second **/ STATIC INT32 CompareRegionSpaOffsetInISet( IN VOID *pFirst, IN VOID *pSecond ) { NVM_COOKIE_DATA *pOffsetFirst = NULL; NVM_COOKIE_DATA *pOffsetSecond = NULL; if (pFirst == NULL || pSecond == NULL) { NVDIMM_DBG("NULL pointer found."); return 0; } pOffsetFirst = (NVM_COOKIE_DATA *) pFirst; pOffsetSecond = (NVM_COOKIE_DATA *) pSecond; if (pOffsetFirst->RegionSpaOffset < pOffsetSecond->RegionSpaOffset) { return -1; } else if (pOffsetFirst->RegionSpaOffset > pOffsetSecond->RegionSpaOffset) { return 1; } else { return 0; } } /** Calculate checksum (called Cookie) from Namespace ranges v1.2 @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pIS InterleaveSet that interleave set cookie will be calculated for @retval EFI_SUCCESS Success @retval EFI_LOAD_ERROR When calculated cookie didn't match @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS CalculateISetCookie( IN ParsedFitHeader *pFitHead, IN OUT NVM_IS *pIS ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; UINT64 Checksum = 0; NVM_COOKIE_DATA ISetCookieData[MAX_NAMESPACE_RANGES]; NVM_COOKIE_DATA TmpCookieData; BOOLEAN ChecksumInserted = FALSE; NvDimmRegionMappingStructure *pNvDimmRegionMappingStructure = NULL; ControlRegionTbl *pControlRegionTable = NULL; DIMM_REGION *pDimmRegion = NULL; LIST_ENTRY *pDimmNode = NULL; UINT32 RegionCount = 0; NVDIMM_ENTRY(); ZeroMem(ISetCookieData, sizeof(ISetCookieData)); ZeroMem(&TmpCookieData, sizeof(TmpCookieData)); if (pFitHead == NULL || pIS == NULL || pIS->pSpaTbl == NULL) { goto Finish; } ReturnCode = GetListSize(&pIS->DimmRegionList, &RegionCount); if (EFI_ERROR(ReturnCode)) { goto Finish; } LIST_FOR_EACH(pDimmNode, &pIS->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pDimmNode); if (pDimmRegion->pDimm == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = GetNvDimmRegionMappingStructureForPid(pFitHead, pDimmRegion->pDimm->DimmID, NULL, TRUE, pIS->pSpaTbl->SpaRangeDescriptionTableIndex, &pNvDimmRegionMappingStructure); if (EFI_ERROR(ReturnCode) || pNvDimmRegionMappingStructure == NULL) { goto Finish; } ReturnCode = GetControlRegionTableForNvDimmRegionTable(pFitHead, pNvDimmRegionMappingStructure, &pControlRegionTable); if (EFI_ERROR(ReturnCode) || pNvDimmRegionMappingStructure == NULL) { goto Finish; } ISetCookieData[Index].RegionSpaOffset = pNvDimmRegionMappingStructure->RegionOffset; ISetCookieData[Index].SerialNum = pControlRegionTable->SerialNumber; ISetCookieData[Index].VendorId = pControlRegionTable->VendorId; ISetCookieData[Index].ManufacturingDate = pControlRegionTable->ManufacturingDate; ISetCookieData[Index].ManufacturingLocation = pControlRegionTable->ManufacturingLocation; Index++; } ReturnCode = BubbleSort(ISetCookieData, RegionCount, sizeof(NVM_COOKIE_DATA), CompareRegionSpaOffsetInISet); if (EFI_ERROR(ReturnCode)) { goto Finish; } ChecksumInserted = ChecksumOperations(&ISetCookieData, sizeof(NVM_COOKIE_DATA) * RegionCount, &Checksum, TRUE); if (!ChecksumInserted) { NVDIMM_DBG("Could not calculate the checksum."); ReturnCode = EFI_LOAD_ERROR; goto Finish; } pIS->InterleaveSetCookie = Checksum; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Calculate checksum (called Cookie) from Namespace ranges for Namespace Labels V1.1 @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pIS InterleaveSet that interleave set cookie will be calculated for @retval EFI_SUCCESS Success @retval EFI_LOAD_ERROR When calculated cookie didn't match @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS CalculateISetCookieVer1_1( IN ParsedFitHeader *pFitHead, IN OUT NVM_IS *pIS ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; UINT64 Checksum = 0; NVM_COOKIE_DATA_1_1 ISetCookieData[MAX_NAMESPACE_RANGES]; NVM_COOKIE_DATA_1_1 TmpCookieData; BOOLEAN ChecksumInserted = FALSE; NvDimmRegionMappingStructure *pNvDimmRegionMappingStructure = NULL; ControlRegionTbl *pControlRegionTable = NULL; DIMM_REGION *pDimmRegion = NULL; LIST_ENTRY *pDimmNode = NULL; UINT32 RegionCount = 0; NVDIMM_ENTRY(); ZeroMem(ISetCookieData, sizeof(ISetCookieData)); ZeroMem(&TmpCookieData, sizeof(TmpCookieData)); if (pFitHead == NULL || pIS == NULL || pIS->pSpaTbl == NULL) { goto Finish; } ReturnCode = GetListSize(&pIS->DimmRegionList, &RegionCount); if (EFI_ERROR(ReturnCode)) { goto Finish; } LIST_FOR_EACH(pDimmNode, &pIS->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pDimmNode); if (pDimmRegion->pDimm == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = GetNvDimmRegionMappingStructureForPid(pFitHead, pDimmRegion->pDimm->DimmID, NULL, TRUE, pIS->pSpaTbl->SpaRangeDescriptionTableIndex, &pNvDimmRegionMappingStructure); if (EFI_ERROR(ReturnCode) || pNvDimmRegionMappingStructure == NULL) { goto Finish; } ReturnCode = GetControlRegionTableForNvDimmRegionTable(pFitHead, pNvDimmRegionMappingStructure, &pControlRegionTable); if (EFI_ERROR(ReturnCode) || pNvDimmRegionMappingStructure == NULL) { goto Finish; } ISetCookieData[Index].RegionSpaOffset = pNvDimmRegionMappingStructure->RegionOffset; ISetCookieData[Index].SerialNum = pControlRegionTable->SerialNumber; Index++; } ReturnCode = BubbleSort(ISetCookieData, RegionCount, sizeof(NVM_COOKIE_DATA_1_1), CompareRegionSpaOffsetInISet); if (EFI_ERROR(ReturnCode)) { goto Finish; } ChecksumInserted = ChecksumOperations(&ISetCookieData, sizeof(NVM_COOKIE_DATA_1_1) * RegionCount, &Checksum, TRUE); if (!ChecksumInserted) { NVDIMM_DBG("Could not calculate the checksum."); ReturnCode = EFI_LOAD_ERROR; goto Finish; } pIS->InterleaveSetCookie_1_1 = Checksum; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Find and assign parent Interleave Set for AppDirect Namespace @param[in,out] pNamespace AppDirect Namespace that Interleave Set will be found for @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_NOT_FOUND Interleave Set has not been found **/ EFI_STATUS FindAndAssignISForNamespace( IN OUT NAMESPACE *pNamespace ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LIST_ENTRY *pIsNode = NULL; NVM_IS *pIs = NULL; LIST_ENTRY *pRegionNode = NULL; LIST_ENTRY *pFreeMapNode = NULL; DIMM_REGION *pRegion = NULL; BOOLEAN ISetFound = FALSE; BOOLEAN RegionFound = FALSE; UINT32 Index = 0; UINT32 DimmRegionListSize = 0; LIST_ENTRY *pFreemapList = NULL; MEMMAP_RANGE *pRange = NULL; UINT64 ISCookie = 0; NVDIMM_ENTRY(); if (pNamespace == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } LIST_FOR_EACH(pIsNode, &gNvmDimmData->PMEMDev.ISs) { pIs = IS_FROM_NODE(pIsNode); ISetFound = FALSE; ISCookie = (pNamespace->Major == NSINDEX_MAJOR && pNamespace->Minor == NSINDEX_MINOR_1) ? pIs->InterleaveSetCookie_1_1 : pIs->InterleaveSetCookie; /** Cookie from label must match the cookie calculated for the interleave set **/ /** Bail out if did not find a match for old labels or new labels **/ if (pNamespace->InterleaveSetCookie != ISCookie) { NVDIMM_DBG("Cookie from label: %llx Cookie from IS: %llx", pNamespace->InterleaveSetCookie, ISCookie); continue; } // Assume that we will find a proper Interleave Set // Let's assign the parent IS to the namespace right now. If the size of the namespace region count does not // match the number of regions on the IS, that means we found broken labels (partial namespace), and we do // want to surface such a namespace with health state as critical. ISetFound = TRUE; pNamespace->pParentIS = pIs; InsertTailList(&pIs->AppDirectNamespaceList, &pNamespace->IsNode); ReturnCode = GetListSize(&pIs->DimmRegionList, &DimmRegionListSize); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Interleave Set and AppDirect Namespace have to have the same number of regions/ranges **/ if (DimmRegionListSize != pNamespace->RangesCount) { continue; } LIST_FOR_EACH(pRegionNode, &pIs->DimmRegionList) { pRegion = DIMM_REGION_FROM_NODE(pRegionNode); pFreemapList = (LIST_ENTRY *) AllocateZeroPool(sizeof(*pFreemapList)); if (pFreemapList == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } InitializeListHead(pFreemapList); ReturnCode = GetDimmFreemap(pRegion->pDimm, FreeCapacityForADMode, pFreemapList); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to get free memory range"); FreeMemmapItems(pFreemapList); FREE_POOL_SAFE(pFreemapList); goto Finish; } RegionFound = FALSE; for (Index = 0; Index < pNamespace->RangesCount && !RegionFound; Index++) { LIST_FOR_EACH(pFreeMapNode, pFreemapList) { pRange = MEMMAP_RANGE_FROM_NODE(pFreeMapNode); /** NS DPA range from label must exist inside a free AD capacity range **/ if (pNamespace->Range[Index].pDimm == pRegion->pDimm && pNamespace->Range[Index].Dpa >= pRange->RangeStartDpa && ((pNamespace->Range[Index].Dpa + pNamespace->Range[Index].Size) <= (pRange->RangeStartDpa + pRange->RangeLength))) { RegionFound = TRUE; break; } } } FreeMemmapItems(pFreemapList); FREE_POOL_SAFE(pFreemapList); if (!RegionFound) { ISetFound = FALSE; break; } } if (ISetFound) { // If the IS is found and mapped correctly, we will get a SPA address for the Namespace ReturnCode = CalculateAppDirectNamespaceBaseSpa(pNamespace, pIs); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to locate base SPA address of Namespace"); goto Finish; } break; } } if (!ISetFound) { ReturnCode = EFI_NOT_FOUND; goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Calculate actual Namespace size on Dimm @param[in] NamespaceType Type of the namespace to be created (Block or PM). @param[in] BlockSize the size of each of the block in the device. @param[in] Capacity usable capacity @param[in] Mode - boolean value to decide when the block namespace should have the BTT arena included @param[out] pActualBlockCount actual block count that needs to be occupied on Dimm @param[out] pActualCapacity actual capacity that needs to be occupied on Dimm @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS ConvertUsableSizeToActualSize( IN UINT32 BlockSize, IN UINT64 Capacity, IN BOOLEAN Mode, OUT UINT64 *pActualBlockCount, OUT UINT64 *pActualCapacity, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT64 ActualBlockCount = 0; UINT64 ActualCapacity = Capacity; NVDIMM_ENTRY(); if (pActualBlockCount == NULL || pActualCapacity == NULL || pCommandStatus == NULL) { goto Finish; } // In case we have a non pow(2) block size, we might need to make a proper block alignment if (ActualCapacity % BlockSize != 0) { ActualCapacity = ROUNDUP(ActualCapacity, BlockSize); } ActualBlockCount = ActualCapacity / BlockSize; if (ActualCapacity == 0) { ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_NAMESPACE_CAPACITY); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pActualBlockCount = ActualBlockCount; *pActualCapacity = ActualCapacity; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check if there is at least one Namespace on specified Dimms. @param[in] pDimms Array of Dimm pointers @param[in] DimmIdsCount Number of items in array @param[out] pFound Output variable saying if there is Namespace or not @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ EFI_STATUS IsNamespaceOnDimms( IN DIMM *pDimms[], IN UINT32 DimmsNum, OUT BOOLEAN *pFound ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Index = 0; UINT32 Index2 = 0; UINT32 NamespacesNum = 0; NVDIMM_ENTRY(); if (pDimms == NULL || pFound == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pFound = FALSE; for (Index = 0; Index < DimmsNum; Index++) { if (pDimms[Index] == NULL) { ReturnCode = EFI_ABORTED; goto Finish; } for (Index2 = 0; Index2 < pDimms[Index]->ISsNum; Index2++) { ReturnCode = GetListSize(&pDimms[Index]->pISs[Index2]->AppDirectNamespaceList, &NamespacesNum); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (NamespacesNum > 0) { *pFound = TRUE; break; } } if (*pFound) { break; } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve mapped AppDirect memory from NFIT. @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in, out] pIS Interleave Set to retrieve AppDirect I/O structures for @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_NOT_FOUND SPA table has not been found **/ EFI_STATUS RetrieveAppDirectMappingFromNfit( IN ParsedFitHeader *pFitHead, IN OUT NVM_IS *pIS ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; UINT32 Index2 = 0; SpaRangeTbl *pSpa = NULL; NvDimmRegionMappingStructure *pNvDimmRegion = NULL; LIST_ENTRY *pNode = NULL; DIMM_REGION *pRegion = NULL; BOOLEAN SameRegionOffset = FALSE; BOOLEAN SameRegionSize = FALSE; BOOLEAN MatchedDimmFound = FALSE; UINT32 MatchedDimmsNum = 0; BOOLEAN MatchedSpaFound = FALSE; UINT32 DimmsNumInIS = 0; NVDIMM_ENTRY(); if (pFitHead == NULL || pIS == NULL) { goto Finish; } if (pIS->pSpaTbl != NULL) { goto Finish; } ReturnCode = GetListSize(&pIS->DimmRegionList, &DimmsNumInIS); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Match Interleave Set with SPA table **/ for (Index = 0; Index < pFitHead->SpaRangeTblesNum; Index++) { pSpa = pFitHead->ppSpaRangeTbles[Index]; /** Start with TRUE. If everything will be matched and valid, it will stay as TRUE. **/ MatchedSpaFound = TRUE; MatchedDimmsNum = 0; for (Index2 = 0; Index2 < pFitHead->NvDimmRegionMappingStructuresNum; Index2++) { pNvDimmRegion = pFitHead->ppNvDimmRegionMappingStructures[Index2]; if (pSpa->SpaRangeDescriptionTableIndex == pNvDimmRegion->SpaRangeDescriptionTableIndex) { MatchedDimmFound = FALSE; LIST_FOR_EACH(pNode, &pIS->DimmRegionList) { pRegion = DIMM_REGION_FROM_NODE(pNode); if (pRegion->pDimm->DimmID == pNvDimmRegion->NvDimmPhysicalId) { SameRegionOffset = pRegion->pDimm->PmStart + pRegion->PartitionOffset == pNvDimmRegion->NvDimmPhysicalAddressRegionBase; SameRegionSize = pRegion->PartitionSize == pNvDimmRegion->NvDimmRegionSize; if (SameRegionOffset && SameRegionSize) { MatchedDimmFound = TRUE; MatchedDimmsNum += 1; pRegion->SpaRegionOffset = pNvDimmRegion->RegionOffset; } break; } } if (!MatchedDimmFound) { MatchedSpaFound = FALSE; break; } } } if (MatchedSpaFound && MatchedDimmsNum == DimmsNumInIS) { /** The matched SPA has been found **/ pIS->pSpaTbl = pSpa; break; } } if (pIS->pSpaTbl != NULL) { ReturnCode = EFI_SUCCESS; } else { ReturnCode = EFI_NOT_FOUND; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get block size of block device for Namespace @param[in] pNamespace Namespace that block size of block device will be retrieved for @retval Block size **/ UINT64 GetBlockDeviceBlockSize( IN NAMESPACE *pNamespace ) { UINT64 BlockSize = 0; NVDIMM_ENTRY(); if (pNamespace == NULL) { goto Finish; } BlockSize = pNamespace->Media.BlockSize; Finish: NVDIMM_EXIT(); return BlockSize; } /** Get persistent memory type for Namespace @param[in] pNamespace Namespace that persistent memory type will be determined for @param[out] pPersistentMemType Persistent memory type @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ EFI_STATUS GetPersistentMemoryType( IN NAMESPACE *pNamespace, OUT UINT8 *pPersistentMemType ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LIST_ENTRY *pMemmapList = NULL; BOOLEAN Interleaved = FALSE; UINT32 RegionsNum = 0; if (pNamespace == NULL || pPersistentMemType == NULL) { goto Finish; } if (pNamespace->NamespaceType == APPDIRECT_NAMESPACE) { /** No interleave set found for the AppDirect Namespace **/ if (pNamespace->pParentIS == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = GetListSize(&pNamespace->pParentIS->DimmRegionList, &RegionsNum); if (EFI_ERROR(ReturnCode)) { goto Finish; } Interleaved = RegionsNum > 1; *pPersistentMemType = (Interleaved) ? PM_TYPE_AD : PM_TYPE_AD_NI; } ReturnCode = EFI_SUCCESS; Finish: if (pMemmapList != NULL) { FreeMemmapItems(pMemmapList); FREE_POOL_SAFE(pMemmapList); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get accessible capacity in bytes @param[in] pNamespace Namespace that accessible capacity will be calculated for @retval Accessible capacity **/ UINT64 GetAccessibleCapacity( IN NAMESPACE *pNamespace ) { return pNamespace->BlockSize * pNamespace->BlockCount; } /** Get raw capacity in bytes @param[in] pNamespace Namespace that raw capacity will be calculated for @retval Raw capacity **/ UINT64 GetRawCapacity( IN NAMESPACE *pNamespace ) { return GetPhysicalBlockSize(pNamespace->BlockSize) * pNamespace->BlockCount; } #ifndef OS_BUILD /** Checks to see if a given address block collides with one or more of the addresses BIOS has marked as bad @param[in] Address an array of bad addresses as returned from BIOS @param[in] Length the number of bad addresses @retval EFI_SUCCESS If the range does not collide @retval EFI_DEVICE_ERROR If the range is found to collide with one or more addresses from BIOS **/ EFI_STATUS IsAddressRangeInArsList( IN UINT64 Address, IN UINT64 Length ) { EFI_STATUS ReturnCode = EFI_SUCCESS; INT32 Index = 0; UINT64 BadBlockStart = 0; UINT64 BadBlockEnd = 0; UINT64 CheckBlockStart = Address; UINT64 CheckBlockEnd = Address + Length - 1; if (gArsBadRecordsCount == 0) { return ReturnCode; } NVDIMM_ENTRY(); if (gArsBadRecordsCount < 0) { ReturnCode = LoadArsList(); if (EFI_PROTOCOL_ERROR == ReturnCode) { ReturnCode = EFI_SUCCESS; NVDIMM_DBG("Failed to load the ARS list (protocol missing?) Cannot check for collisions.\n"); goto Finish; } } if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to load the ARS list. Cannot check for collisions.\n"); goto Finish; } if (gArsBadRecordsCount == 0) { NVDIMM_DBG("There are no bad addresses in the ARS list.\n"); goto Finish; } if (NULL != gArsBadRecords && gArsBadRecordsCount > 0) { //Check that address range does not contain one of the bad addresses identified by BIOS for (Index = 0; Index < gArsBadRecordsCount; Index++) { BadBlockStart = gArsBadRecords[Index].SpaOfErrLoc; BadBlockEnd = gArsBadRecords[Index].SpaOfErrLoc + gArsBadRecords[Index].Length - 1; NVDIMM_DBG("Checking address 0x%llx, len 0x%llx against bad ARS address 0x%llx, len 0x%llx", Address, Length, gArsBadRecords[Index].SpaOfErrLoc, gArsBadRecords[Index].Length); if ((CheckBlockStart >= BadBlockStart && CheckBlockStart <= BadBlockEnd) || //the request starts at an address in the bad range (CheckBlockStart >= BadBlockStart && CheckBlockEnd <= BadBlockEnd) || //the request fits entirely in a bad range (BadBlockStart >= CheckBlockStart && BadBlockEnd <= CheckBlockEnd) || //the bad range fits entirely in the check range (CheckBlockEnd >= BadBlockStart && CheckBlockEnd <= BadBlockEnd)) //the request ends at an address in the bad range { ReturnCode = EFI_DEVICE_ERROR; NVDIMM_DBG("Collision detected with the ARS list"); goto Finish; } } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Namespace.h000066400000000000000000001017041440615110200213730ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _NAMESPACE_H_ #define _NAMESPACE_H_ #include #include #include "Dimm.h" #include "Region.h" #include #include "Btt.h" #include #include "Pfn.h" /** BTT Arena Info defines **/ #define BTT_ALIGNMENT 4096 //!< Alignment of all BTT structures #define BTTINFO_SIG_LEN 16 #pragma pack(push) #pragma pack(1) #define NAMESPACE_SIGNATURE SIGNATURE_64('N', 'A', 'M', 'E', 'S', 'P', 'C', 'E') #define NAMESPACE_FROM_NODE(a, FieldName) CR(a, NAMESPACE, FieldName, NAMESPACE_SIGNATURE) #define BYTE_MASK 0xFF #define BYTE_SHIFT 8 #define CREATE_NAMESPACE_ID(RegionId, NamespaceIndex) (((UINT8) (RegionId & BYTE_MASK)) << BYTE_SHIFT) + (UINT8)(NamespaceIndex & BYTE_MASK) typedef struct { DIMM *pDimm; UINT64 Dpa; UINT64 Size; } NAMESPACE_REGION; typedef struct _NAMESPACE { UINT64 Signature; UINT64 SpaNamespaceBase; LIST_ENTRY NamespaceNode; LIST_ENTRY IsNode; LIST_ENTRY DimmNode; EFI_BLOCK_IO_MEDIA Media; EFI_HANDLE BlockIoHandle; LABEL_FLAGS Flags; UINT16 NamespaceId; UINT8 NamespaceGuid[NSGUID_LEN]; UINT16 HealthState; UINT64 BlockSize; UINT64 BlockCount; BOOLEAN Enabled; UINT8 NamespaceType; DIMM *pParentDimm; NVM_IS *pParentIS; UINT32 RangesCount; UINT16 Major; UINT16 Minor; NAMESPACE_REGION Range[MAX_NAMESPACE_RANGES]; BOOLEAN ProtocolsInstalled; EFI_BLOCK_IO_PROTOCOL BlockIoInstance; EFI_DEVICE_PATH *pBlockDevicePath; EFI_UNICODE_STRING_TABLE *pNamespaceName; BOOLEAN IsBttEnabled; BTT *pBtt; UINT8 Name[NLABEL_NAME_LEN_WITH_TERMINATOR]; UINT64 InterleaveSetCookie; BOOLEAN IsPfnEnabled; PFN *pPfn; BOOLEAN IsRawNamespace; UINT64 UsableSize; } NAMESPACE; typedef struct _NVM_COOKIE_DATA { UINT64 RegionSpaOffset; UINT32 SerialNum; UINT16 VendorId; UINT16 ManufacturingDate; UINT8 ManufacturingLocation; UINT8 Reserved[31]; } NVM_COOKIE_DATA; typedef struct _NVM_COOKIE_DATA_1_1 { UINT64 RegionSpaOffset; UINT32 SerialNum; UINT32 Reserved; } NVM_COOKIE_DATA_1_1; #pragma pack(pop) /** Definitions for flags mask for BttArenaInfo structure above. **/ #define BTTINFO_FLAG_ERROR 0x00000001 //!< Error state (read-only) #if defined(_MSC_VER) #pragma warning( push ) #pragma warning( disable : 4200 ) #endif typedef struct { UINT32 PreMapLBA[0]; } BTT_MAP; #if defined(_MSC_VER) #pragma warning( pop ) #endif EFI_STATUS IsNamespaceLocked( IN NAMESPACE *pNamespace, OUT BOOLEAN *pIsLocked ); /** Clean the namespaces list @param[in,out] pList: the list to clean **/ VOID CleanNamespacesList( IN OUT LIST_ENTRY *pList ); /** Iterates over all of the existing namespaces and installs block io protocols This function will install two protocols: block and device path on the namespace handle. Also it will allocate memory for the block device (child) device path instance so it can be attached to the Controller (provided by the parameter). At last, the function will open the Controllers device path instance as a child device. Before unloading the driver, CleanNamespaces should be run to close, uninstall those protocols and to free the memory. @retval EFI_SUCCESS the iteration and installation were successful. Other errors from InstallMultipleProtocolInterfaces and OpenProtocol functions. Warning! This function does not exit at the first error. It will be logged and then it continues to next namespace, trying to create as much working block devices as it is possible. **/ EFI_STATUS EFIAPI InstallProtocolsOnNamespaces( ); /** This function closes all protocols opened by the block devices handles, it will also uninstall the block and device path protocols. At the end it deallocated the memory taken for the device path protocol instance. @retval EFI_SUCCESS the cleanup completed successfully. Other errors returned from UninstallMultipleProtocolInterfaces function. Warning! In case of an error, the cleanup does not break, it tries to clean the rest of the DIMMs. **/ EFI_STATUS EFIAPI CleanNamespaces( ); /** Attaches the block device to the DIMM and installs the Block IO Protocol to the Namespace. @param[in,out] pNamespace - is the pointer to the NAMESPACE structure that is supposed to be exposed in the system. @retval EFI_INVALID_PARAMETER if the pNamespace equals NULL. @retval EFI_ABORTED if the parent DIMM block window was not properly initialized or the DIMM security state could not be determined. @retval EFI_NOT_READY if the Namespace was not Enabled. @retval EFI_ACCESS_DENIED if the DIMM is locked @retval EFI_SUCCESS if the operation completed successfully. Or the Namespace is not Block. Other return values from OpenProtocol and InstallMultipleProtocolInterfaces function. **/ EFI_STATUS InstallNamespaceProtocols( IN OUT NAMESPACE *pNamespace ); /** Detaches the block device from the DIMM and uninstalls the Block IO Protocol from the Namespace. If the Namespace is not enabled, no actions are taken. @param[in,out] pNamespace - is the pointer to the NAMESPACE structure that is supposed to be removed from the system. @retval EFI_INVALID_PARAMETER if the pNamespace equals NULL. @retval EFI_ABORTED if the Block IO instance handle equals NULL and no actions can be performed. @retval EFI_SUCCESS if the operation completed successfully. Or the Namespace is not Block. Other return values from UninstallMultipleProtocolInterfaces function. **/ EFI_STATUS UninstallNamespaceProtocols( IN OUT NAMESPACE *pNamespace ); /** Performs a block read or write to the Namespace. The function calculates the proper DPA DIMM offset and issues the proper read or write operation on the destination DIMM. @param[in] pNamespace The Intel NVM Dimm Namespace to perform the IO Block operation. @param[in] Lba the Logica Block Addressing block offset to perform the IO on. @param[out] pBuffer the destination/source buffer where or from the data will be copied. @param[in] BlockLength the length of the buffer - should equal to the namespace block size. @param[in] ReadOperation boolean value indicating what type of IO is requested. TRUE means a read operation, and FALSE results in write operation. @retval EFI_SUCCESS if the IO operation was performed without errors. @retval Other return codes from functions: DimmRead, DimmWrite, AppDirectIo **/ EFI_STATUS IoNamespaceBlock( IN NAMESPACE *pNamespace, IN CONST UINT64 Lba, OUT CHAR8 *pBuffer, IN CONST UINT32 BlockLength, IN CONST BOOLEAN ReadOperation ); /** Performs a read or write to the AppDirect Namespace. The data is read/written from/to Interleave Set mapped in system memory. @param[in] pNamespace Intel NVM Dimm Namespace to perform the IO operation. @param[in] Offset Offset of AppDirect Namespace @param[in, out] pBuffer Destination/source buffer where or from the data will be copied. @param[in] Nbytes Number of bytes to read/write @param[in] ReadOperation boolean value indicating what type of IO is requested. @retval EFI_SUCCESS If the IO operation was performed without errors. @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS AppDirectIo( IN NAMESPACE *pNamespace, IN UINT64 Offset, IN OUT CHAR8 *pBuffer, IN UINT64 Nbytes, IN BOOLEAN ReadOperation ); /** Performs a buffer read or write to the namespace. The function calculates the proper DPA DIMM offset and issues the proper read or write operation on the destination DIMM. @param[in] pNamespace The Intel NVM Dimm Namespace to perform the IO buffer operation. @param[in] Offset - byte offset on the Namespace to perform the IO on - should be a multiple of cache line size. @param[out] pBuffer the destination/source buffer where or from the data will be copied. @param[in] BufferLength the length of the buffer - should be a multiple of cache line size. @param[in] ReadOperation boolean value indicating what type of IO is requested. TRUE means a read operation, and FALSE results in write operation. @retval EFI_SUCCESS if the IO operation was performed without errors. @retval EFI_INVALID_PARAMETER if pNamespace and/or pBuffer equals NULL. @retval EFI_BAD_BUFFER_SIZE if Offset and/or BufferLength are not aligned to the cache line size. @retval Other return codes from functions: DimmRead, DimmWrite, AppDirectIo **/ EFI_STATUS IoNamespaceBytes( IN NAMESPACE *pNamespace, IN UINT64 Offset, OUT CHAR8 *pBuffer, IN UINT32 BufferLength, IN BOOLEAN ReadOperation ); /** Copies the Raw LSA Index data into proper data structures @param[in] pRawData Raw data from the PCD @param[in] PcdLsaPartitionSize for checking Index size @param[out] pLsa The Label Storage Area structure @retval EFI_INVALID_PARAMETER Invalid parameter passed @retval EFI_SUCCESS Data was copied successfully **/ EFI_STATUS RawDataToLabelIndexArea( IN UINT8 *pRawData, IN UINT32 PcdLsaPartitionSize, OUT LABEL_STORAGE_AREA *pLsa ); /** Reads Label Storage Area of a specified DIMM. Function reads Platform Config Data partition 3. of a DIMM and invokes validation subroutine to check for data consistency. Required memory will be allocated, it is caller responsibility to free it after it is no longer needed. @param[in] DimmPid Dimm ID of DIMM from which to read the data @param[out] ppLsa Pointer with address at which memory LSA data will be stored. @retval EFI_INVALID_PARAMETER NULL pointer provided as a parameter @retval EFI_DEVICE_ERROR Unable to retrieve data from DIMM or retrieved data is not valid @retval EFI_SUCCESS Valid LSA retrieved **/ EFI_STATUS ReadLabelStorageArea( IN UINT16 DimmPid, OUT LABEL_STORAGE_AREA **ppLsa ); /** Writes Label Storage Area to a specified DIMM. Function invokes validation subroutine to check provided data consistency and then stores LSA on Platform Config Data partition 3. of a DIMM @param[in] DimmPid Dimm ID of DIMM on which to write LSA @param[in] pLsa Pointer with LSA structure @retval EFI_INVALID_PARAMETER No data provided or the data is invalid @retval EFI_DEVICE_ERROR Unable to store data on a DIMM @retval EFI_SUCCESS LSA written correctly **/ EFI_STATUS WriteLabelStorageArea( IN UINT16 DimmPid, IN LABEL_STORAGE_AREA *pLsa ); /** Zero the Label Storage Area Header on the specified DIMM. @param[in] DimmPid Dimm ID of DIMM on which to write LSA @retval EFI_INVALID_PARAMETER No data provided or the data is invalid @retval EFI_OUT_RESOURCES Unable to allocate resources @retval EFI_SUCCESS LSA written correctly **/ EFI_STATUS ZeroLabelStorageAreaHeader( IN UINT16 DimmPid ); /** Initialize a random seed using current time. Get current time first. Then initialize a random seed based on some basic mathematics operation on the hour, day, minute, second, nanosecond and year of the current time. @return The random seed initialized with current time. @return 0 if there was an error while getting the current time. **/ UINT32 EFIAPI GenerateCurrentTimeSeed( VOID ); /** Generate random numbers in a buffer. @param[in, out] Rand The buffer to contain random numbers. @param[in] RandLength The length of the Rand buffer. **/ VOID RandomizeBuffer( IN OUT UINT8 *Rand, IN UINT64 RandLength ); /** Generate a NamespaceId value @retval The generated ID **/ UINT16 EFIAPI GenerateNamespaceId( IN UINT16 RegionId ); /** Generate a random (version 4) GUID @retval The generated GUID @retval Zeroed buffer, if there was a problem while getting the time seed **/ VOID EFIAPI GenerateRandomGuid( OUT GUID *pResultGuid ); /** GetNamespaceByName Looks through namespaces list searching for a namespace with a particular name. @param[in] pName Target Name of the Namespace that the function is searching for. @retval NAMESPACE structure pointer if Namespace has been found @retval NULL pointer if not found **/ NAMESPACE* GetNamespaceByName( IN CHAR8* pName ); /** Looks through namespaces list searching for a namespace with a particular id. @param[in] NamespaceId Target ID of the Namespace that the function is searching for. @retval NAMESPACE structure pointer if Namespace has been found @retval NULL pointer if not found **/ NAMESPACE* GetNamespaceById( IN UINT16 NamespaceId ); /** Read data from an Intel NVM Dimm Namespace. Transform LBA into RDPA and call the Intel NVM Dimm read function. The function reads the block size, so the input buffer size needs to be at least the namespace block size. @param[in] pNamespace The Intel NVM Dimm Namespace to read data from @param[in] Lba LBA of the start of the data region to read @param[out] pBuffer Buffer to place read data into @retval Return values from IoNamespaceBlock function **/ EFI_STATUS ReadNamespaceBlock( IN NAMESPACE *pNamespace, IN UINT64 Lba, OUT CHAR8 *pBuffer ); /** Write data to an Intel NVM Dimm Namespace. Transform LBA into RDPA and call the Intel NVM Dimm DIMM write function. The function writes the block size, so the input buffer size needs to be at least the namespace block size. @param[in] pNamespace The Intel NVM Dimm Namespace to write data to @param[in] Lba LBA of the start of the data region to write @param[in] pBuffer Buffer with data to write @retval EFI_SUCCESS on a successful write @retval Error return values from IoNamespaceBlock function **/ EFI_STATUS WriteNamespaceBlock( IN NAMESPACE *pNamespace, IN UINT64 Lba, IN CHAR8 *pBuffer ); /** Read block from the namespace The function reads only one block from the device, the buffer length needs to be at least the Namespace block size. @param[in] pNamespace Namespace that data will be read from @param[in] Lba LBA to retrieve read from @param[out] pBuffer pointer to the memory where the result should be stored @retval EFI_SUCCESS on a successful read @retval Error return values from BttRead or ReadNamespaceBlock function **/ EFI_STATUS ReadBlockDevice( IN NAMESPACE *pNamespace, IN UINT64 Lba, OUT CHAR8 *pBuffer ); /** Write to the namespace through the BTT mechanism. The function writes only one block from the device, the buffer length needs to be at least the Namespace block size. @param[in] pNamespace Namespace containing the BTT @param[in] Lba LBA to store the write to @param[out] pBuffer pointer to the memory where the destination data resides @retval EFI_SUCCESS on a successful write @retval Error return values from BttWrite or WriteNamespaceBlock function **/ EFI_STATUS WriteBlockDevice( IN NAMESPACE *pNamespace, IN UINT64 Lba, OUT CHAR8 *pBuffer ); /** Checks whether NamespaceType is AppDirect Function reads the label version and NameSpaceLabel and determines whether App Direct Type or not @retval TRUE if AppDirect Type @retval FALSE if not AppDirect Type **/ BOOLEAN IsNameSpaceTypeAppDirect(IN NAMESPACE_LABEL *pNamespaceLabel, IN BOOLEAN Is_Namespace1_1 ); /* Checks if Lsa status of Dimms is not initialized for a manageable dimm @retval TRUE - if a manageable dimm has lsaStatus set to LSA_NOT_INIT */ BOOLEAN IsLsaNotInitializedOnADimm(); /** Initializes Namespaces inventory Function reads LSA data from all DIMMs, then scans for Namespaces data in it. All found Namespaces are stored in a list in global PMEMDev structure. @retval EFI_DEVICE_ERROR Reading LSA data failed @retval EFI_ABORTED Reading Namespaces data from LSA failed @retval EFI_SUCCESS Namespaces inventory correctly initialized **/ EFI_STATUS InitializeNamespaces( ); /** Aligns the size of the Label Index area and calculates the number of free blocks, padding the driver can support. @param[in] PcdSize Size of the PCD Partition area @param[in] UseLatestLabelVersion Use latest version of Labels @param[out] pFreeBlocks The size of free array @param[out] pPadding The padding required to complete the index area @retval EFI_INVALID_PARAMETER null parameter provided @retval EFI_SUCCESS Alignment went well **/ EFI_STATUS AlignLabelStorageArea( IN UINT32 PcdSize, IN BOOLEAN UseLatestLabelVersion, OUT UINT32 *pFreeBlocks, OUT UINT32 *pPadding ); /** Gets the Label Index Data in a contiguous memory @param[in] pLsa The Label Storage Area @param[in] LabelIndex The index of the LSA @param[out] pRawData Pointer to the contiguous memory region @retval EFI_INVALID_PARAMETER NULL pointer provided @retval EFI_OUT_OF_RESOURCES could not allocate memory @retval EFI_SUCCESS Successfully transferred memory **/ EFI_STATUS LabelIndexAreaToRawData( IN LABEL_STORAGE_AREA *pLsa, IN UINT32 LabelIndex, OUT UINT8 **ppRawData ); /** Check if LSA of a specified DIMM is initialized. If empty LSA is detected then it is initialized. If non empty LSA is detected then it is validated for data correctness. @param[in] pDimm Target DIMM @param[in] LabelVersionMajor Major version of label to init @param[in] LabelVersionMinor Minor version of label to init @param[in] ForceInitialization If true, always create a new LSA @retval EFI_INVALID_PARAMETER NULL pointer provided @retval EFI_VOLUME_CORRUPTED LSA data is broken @retval EFI_SUCCESS Valid LSA detected or initialized correctly **/ EFI_STATUS InitializeLabelStorageArea( IN DIMM *pDimm, IN UINT16 LabelVersionMajor, IN UINT16 LabelVersionMinor, IN BOOLEAN ForceInitialization ); /** Initialize all label storage areas @param[in] ppDimms Array of DIMMs @param[in] DimmsNum Number of DIMMs @param[in] LabelVersionMajor Major version of label to init @param[in] LabelVersionMinor Minor version of label to init @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pDimmList is NULL @retval return codes from SendConfigInputToDimm **/ EFI_STATUS ClearAndInitializeAllLabelStorageAreas( IN DIMM **ppDimms, IN UINT32 DimmsNum, IN UINT16 LabelVersionMajor, IN UINT16 LabelVersionMinor, OUT COMMAND_STATUS *pCommandStatus ); /** Find the Memory map range with the lowest DPA that contains a given region size in a given IS. IS that is passed in is of the type the user wishes to create NS on Size has already been aligned for NS requirements If size is MAX_UINT64 ignore lowest DPA and instead find the largest free memory region for that exists on all dimms in the interleave set @param[in] pIS pointer to Interleave Set that the memory range is contained in. @param[in] Size Minimum size the memory map range must be, if MAX_UINT64 then find the largest possible. @param[out] pFoundRange pointer memory range structure range will be copied into @retval EFI_SUCCESS everything went fine @retval EFI_OUT_OF_RESOURCES when memory allocation fails @retval EFI_INVALID_PARAMETER if any of the parameters is a NULL. @retval EFI_NOT_FOUND could not find a region of given size in IS Other return codes from functions: GetDimmFreemap GetListSize **/ EFI_STATUS FindADMemmapRangeInIS( IN NVM_IS *pIS, IN UINT64 Size, OUT MEMMAP_RANGE *pFoundRange ); /** Namespace Capacity Alignment Aligning the Namespace Capacity Helper Function @param [in] NamespaceCapacity Namespace Capacity provided @param [in] DimmCount @param [out] *pAlignedNamespaceCapacity Total Namespace Capacity after alignment @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_SUCCESS Namespace capacity aligned **/ EFI_STATUS AlignNamespaceCapacity( IN UINT64 NamespaceCapacity, IN UINT64 DimmCount, OUT UINT64* pAlignedNamespaceCapacity ); /** Provision Namespace capacity on a DIMM or Interleave Set. Only one of target pointers must be provided, other one must be NULL. Function analyses possibility of allocation of NamespaceCapacity bytes on a DIMM or on an Interleave Set. If there is enough free space function assigns appropriate Namespace regions. @param[in] pDimm DIMM structure pointer in case of Block Mode Namespaces @param[in] pIS Interleave Set structure pointer in case of Persistent Memory Namespaces @param[in out] NamespaceCapacity Required capacity in bytes @param[out] pNamespace Namespace which regions will be updated if allocation is successful @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_OUT_OF_RESOURCES Not enough free space on target @retval EFI_SUCCESS Namespace capacity allocated **/ EFI_STATUS AllocateNamespaceCapacity( IN DIMM *pDimm, OPTIONAL IN NVM_IS *pIS, OPTIONAL IN OUT UINT64 *pNamespaceCapacity, OUT NAMESPACE *pNamespace ); /** This function tries to match the given EFI_HANDLE in the list of existing block namespace handles. @param[in] Handle - the EFI_HANDLE that the caller needs the NAMESPACE structure pointer to. @retval NULL - if the handle did not match any installed handles. @retval pointer to the result NAMESPACE structure. **/ NAMESPACE * HandleToNamespace( IN EFI_HANDLE Handle ); /** Function updates sequence number and checksum of a next Index Block of the specified Label Storage Area. @param[in] pLsa Target Label Storage Area structure @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_SUCCESS Operation successful **/ EFI_STATUS UpdateLsaIndex( IN LABEL_STORAGE_AREA *pLsa ); /** Check if the label version on the DIMM is new @param[in] pDimm The target DIMM @param[out] pHasLatestIndexVersion Pointer to boolean if the index is v1.2 @retval EFI_INVALID_PARAMETER Null parameter is passed @retval EFI_NOT_FOUND If no index blocks found on the DIMM @retval EFI_SUCCESS If found the label version successfully **/ EFI_STATUS CheckDimmNsLabelVersion( IN DIMM *pDimm, OUT BOOLEAN *pHasLatestIndexVersion ); /** Check if we can use the latest index block version @param[in] pNamespace The target namespace @param[in out] pUseLatestVersion Returns if the LSA uses new version TRUE if all DIMMs in the Namespace have same Major/Minor version of index blocks. No index blocks is treated as version 1.2 FALSE if the DIMMs in the Namespace have a mismatch of the Major/Minor versions in the index block @retval EFI_INVALID_PARAMETER Null parameter is passed @retval EFI_DEVICE_ERROR If the versions don't match on the DIMMs @retval EFI_OUT_OF_RESOURCES Memory allocation failed @retval EFI_SUCCESS Retrieved versions successfully **/ EFI_STATUS UseLatestNsLabelVersion( IN NVM_IS *pIS, IN DIMM *pParentDimm, OUT BOOLEAN *pUseLatestVersion ); /** Creates the namespace labels based on the version of the labels @param[in] pNamespace The target namespace @param[in] Index The index of the label in the namespace @param[in] UseLatestVersion Use latest version of labels @param[in out] pLabel The output label @retval EFI_INVALID_PARAMETER Null parameter passed @retval EFI_SUCCESS Created new label successfully **/ EFI_STATUS CreateNamespaceLabels( IN NAMESPACE *pNamespace, IN UINT16 Index, IN BOOLEAN UseLatestVersion, IN OUT NAMESPACE_LABEL *pLabel ); /** Insert Namespace labels into a Label Storage Area of a DIMM. Function updates all required fields, index bitmap, sequence numbers, etc. Finally Label Storage Area is written to the DIMM. @param[in] pDimm DIMM structure pointer to which insert the labels @param[in] ppLabel Array of label structures to be inserted @param[in] LabelCount Number of labels in the array @param[in] LabelVersionMajor Major version of label to init @param[in] LabelVersionMinor Minor version of label to init @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_OUT_OF_RESOURCES No more label free slots in LSA @retval EFI_SUCCESS Operation successful **/ EFI_STATUS InsertNamespaceLabels( IN DIMM *pDimm, IN NAMESPACE_LABEL **ppLabel, IN UINT16 LabelCount, IN UINT16 LabelVersionMajor, IN UINT16 LabelVersionMinor ); /** Modify attributes of all Namespace labels identified by UUID on a DIMM. Function updates all required fields, index bitmap, sequence numbers, etc. Finally Label Storage Area is written to the DIMM. @param[in] pDimm DIMM structure pointer to which insert the labels @param[in] pUuid Target Namespace identifier @param[in] pFlags New value of Flags field @param[in] pName New value of name @param[in] LabelsNum the new value for the total labels in the block namespace labels set @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_ABORTED Failed to modify the labels @retval EFI_SUCCESS Operation successful **/ EFI_STATUS ModifyNamespaceLabels( IN DIMM *pDimm, IN GUID *pUuid, IN UINT32 *pFlags, OPTIONAL IN CHAR8 *pName, OPTIONAL IN UINT16 LabelsNum OPTIONAL ); /** Remove all Namespace labels with specified UUID from a DIMM. Function updates all required fields, index bitmap, sequence numbers, etc. Finally Label Storage Area is written to the DIMM. @param[in] pDimm DIMM structure pointer to which insert the labels @param[in] pUuid Target Namespace identifier @param[in] Dpa Limit removal to only one label corresponding to this DPA @retval EFI_INVALID_PARAMETER Invalid set of parameters provided @retval EFI_SUCCESS Operation successful **/ EFI_STATUS RemoveNamespaceLabels( IN DIMM *pDimm, IN GUID *pUuid, IN UINT64 Dpa OPTIONAL ); /** This function calculates the real block and raw size that needs to be allocated on the DIMM in order to allow the caller of his desired block size and raw size. The function checks the Raw size to block size alignment and the block size to cache line size alignment, doing appropriate increasing or decreasing of the Raw Size. At least one of the optional parameters is required to be specified. One of: BlockCount or DesiredRawSize must be specified, the other needs to equal 0. @param[in] DesiredRawSize the raw size of the device (should be multiply of DesiredBlockSize, but if not, the function will decrease it accordingly). @param[in] DesiredBlockSize the required block size that will be aligned to the cache line size. @param[in] BlockCount the input block amount - specified by the caller. @param[out] pRealDimmRawSize pointer to where the real DIMM raw size needed will be stored. @param[out] pRealDimmBlockSize pointer to where the real DIMM block size will be stored. @param[out] pBlockCount pointer to where the calculated block count will be stored. @retval EFI_SUCCESS the parameters are OK and the values were calculated properly. @retval EFI_INVALID_PARAMETER the provided parameters are not passed accordingly to the function header. **/ EFI_STATUS GetRealRawSizeAndRealBlockSize( IN UINT64 DesiredRawSize OPTIONAL, IN UINT32 DesiredBlockSize, IN UINT64 BlockCount OPTIONAL, OUT UINT64 *pRealDimmRawSize OPTIONAL, OUT UINT32 *pRealDimmBlockSize OPTIONAL, OUT UINT64 *pBlockCount OPTIONAL ); /** Read data from an Intel NVM Dimm Namespace. Transform buffer offset into RDPA. Call the Intel NVM Dimm read function. The buffer length and the offset need to be aligned to the cache line size. @param[in] pNamespace The Intel NVM Dimm Namespace to read data from @param[in] Offset bytes offset of the start of the data region to read @param[in] Length the length of the buffer to read @param[out] pBuffer Buffer to place read data into @retval EFI_SUCCESS on a successful read @retval Error return values from IoNamespaceBlock function **/ EFI_STATUS ReadNamespaceBytes( IN NAMESPACE *pNamespace, IN CONST UINT64 Offset, OUT VOID *pBuffer, IN CONST UINT64 Length ); /** Write data to an Intel NVM Dimm Namespace. Transform buffer offset into RDPA. Call the Intel NVM Dimm write function. The buffer length and the offset need to be aligned to the cache line size. @param[in] pNamespace The Intel NVM Dimm Namespace to write data to @param[in] Offset bytes offset of the start of the data region to write @param[in] Length the length of the buffer to write @param[in] pBuffer Buffer with data to write @retval EFI_SUCCESS on a successful write @retval Error return values from IoNamespaceBlock function **/ EFI_STATUS WriteNamespaceBytes( IN NAMESPACE *pNamespace, IN CONST UINT64 Offset, IN VOID *pBuffer, IN CONST UINT64 Length ); /** Calculate checksum (called Cookie) from Namespace ranges @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pIS InterleaveSet that interleave set cookie will be calculated for @retval EFI_SUCCESS Success @retval EFI_LOAD_ERROR When calculated cookie didn't match @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS CalculateISetCookie( IN ParsedFitHeader *pFitHead, IN OUT NVM_IS *pIS ); /** Calculate checksum (called Cookie) from Namespace ranges for Namespace Labels V1.1 @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pIS InterleaveSet that interleave set cookie will be calculated for @retval EFI_SUCCESS Success @retval EFI_LOAD_ERROR When calculated cookie didn't match @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS CalculateISetCookieVer1_1( IN ParsedFitHeader *pFitHead, IN OUT NVM_IS *pIS ); /** Find and assign parent Interleave Set for AppDirect Namespace @param[in,out] pNamespace AppDirect Namespace that Interleave Set will be found for @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_NOT_FOUND Interleave Set has not been found **/ EFI_STATUS FindAndAssignISForNamespace( IN OUT NAMESPACE *pNamespace ); /** Calculate actual Namespace size on Dimm @param[in] BlockSize the size of each of the block in the device. @param[in] Capacity usable capacity @param[in] Mode - boolean value to decide when the block namespace should have the BTT arena included @param[out] pActualBlockCount actual block count that needs to be occupied on Dimm @param[out] pActualCapacity actual capacity that needs to be occupied on Dimm @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS ConvertUsableSizeToActualSize( IN UINT32 BlockSize, IN UINT64 Capacity, IN BOOLEAN Mode, OUT UINT64 *pActualBlockCount, OUT UINT64 *pActualCapacity, OUT COMMAND_STATUS *pCommandStatus ); /** Check if there is at least one Namespace on specified Dimms. @param[in] pDimms Array of Dimm pointers @param[in] DimmIdsCount Number of items in array @param[out] pFound Output variable saying if there is Namespace or not @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ EFI_STATUS IsNamespaceOnDimms( IN DIMM *pDimms[], IN UINT32 DimmsNum, OUT BOOLEAN *pFound ); /** Retrieve mapped AppDirect memory from NFIT. @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in, out] pIS Interleave Set to retrieve AppDirect I/O structures for @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_NOT_FOUND SPA table has not been found **/ EFI_STATUS RetrieveAppDirectMappingFromNfit( IN ParsedFitHeader *pFitHead, IN OUT NVM_IS *pIS ); /** Get block size of block device for Namespace @param[in] pNamespace Namespace that block size of block device will be retrieved for @retval Block size **/ UINT64 GetBlockDeviceBlockSize( IN NAMESPACE *pNamespace ); /** Get persistent memory type for Namespace @param[in] pNamespace Namespace that persistent memory type will be determined for @param[out] pPersistentMemType Persistent memory type @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ EFI_STATUS GetPersistentMemoryType( IN NAMESPACE *pNamespace, OUT UINT8 *pPersistentMemType ); /** Get accessible capacity in bytes @param[in] pNamespace Namespace that accessible capacity will be calculated for @retval Accessible capacity **/ UINT64 GetAccessibleCapacity( IN NAMESPACE *pNamespace ); /** Get raw capacity in bytes @param[in] pNamespace Namespace that raw capacity will be calculated for @retval Raw capacity **/ UINT64 GetRawCapacity( IN NAMESPACE *pNamespace ); #ifndef OS_BUILD /** Checks to see if a given address block collides with one or more of the addresses BIOS has marked as bad @param[in] Address The base address @param[in] Length The address range size @retval EFI_SUCCESS If the range does not collide with an ARS address @retval EFI_DEVICE_ERROR If the range is found to collide with one or more addresses from BIOS **/ EFI_STATUS IsAddressRangeInArsList( IN UINT64 Address, IN UINT64 Length ); #endif #endif /** _NAMESPACE_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/driver/Core/NvmDimmPassThru.h000066400000000000000000001461521440615110200225460ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _NVMDIMM_PASS_THRU_H_ #define _NVMDIMM_PASS_THRU_H_ #include #include struct _DIMM; /** Version number is BCD with format: MMmmRr M = Major version number m = minor version number R - Release type (Alpha, Beta, Test, etc) r = release version **/ #define NVMDIMM_PASS_THRU_GENERAL_RELEASE 0 #define NVMDIMM_PASS_THRU_ALPHA_RELEASE 1 #define NVMDIMM_PASS_THRU_BETA_RELEASE 2 #define NVMDIMM_PASS_THRU_TEST_RELEASE 3 #define NVMDIMM_PASS_THRU_MAJOR_VERSION 1 #define NVMDIMM_PASS_THRU_MINOR_VERSION 0 #define NVMDIMM_PASS_THRU_RELEASE_TYPE NVMDIMM_PASS_THRU_TEST_RELEASE #define NVMDIMM_PASS_THRU_RELEASE_NUMBER 0 #define NVMD_PASS_THRU_PROTOCOL_VERSION NVMDIMM_PASS_THRU_MAJOR_VERSION<<16 | \ NVMDIMM_PASS_THRU_MINOR_VERSION<<8 | NVMDIMM_PASS_THRU_RELEASE_TYPE<<4 | \ NVMDIMM_PASS_THRU_RELEASE_NUMBER #define OS_MB_OFFSET 0x100000 //!< Offset from the start of the CTRL region to the start of the OS mailbox #define OS_MB_IN_OFFSET (2 << 20) //!< Offset from the start of the CTRL region to the start of the OS mailbox large input payload #define OS_MB_OUT_OFFSET (3 << 20) //!< Offset from the start of the CTRL region to the start of the OS mailbox large output payload #define REG_SIZE (8) //!< Size of a Intel NVM Dimm Mailbox Register Bytes #define NONCE_SIZE (4) //!< Size of a Intel NVM Dimm Nonce Register Bytes #define PAYLOAD_BETWEEN_SIZE (64) //!< Size between payload[n] and payload[n+1] #define IN_SINGLE_PAYLOAD_SIZE (8) //!< Size of the input payload register #define IN_PAYLOAD_NUM (16) //!< Total number of the input payload registers #define OUT_SINGLE_PAYLOAD_SIZE (8) //!< Size of the output payload register #define OUT_PAYLOAD_NUM (16) //!< Total number of the output payload registers /** Control region offsets **/ #define MB_COMMAND_OFFSET 0 #define MB_NONCE0_OFFSET 0x40 #define MB_NONCE1_OFFSET 0x80 #define MB_IN_PAYLOAD0_OFFSET 0xC0 #define MB_STATUS_OFFSET 0x4C0 #define MB_OUT_PAYLOAD0_OFFSET 0x500 #define DIMM_SN_LEN 20 //!< DIMM Serial Number buffer length #define DIMM_MFR_LEN 20 //!< Manufacturer name buffer length #define DIMM_PN_LEN 20 //!< DIMM Part Number buffer length #define DIMM_FW_REV_LEN 14 //!< DIMM FW Revision buffer length #define SECURITY_NONCE_LEN 8 //!< Length of a security nonce #define BCD_DATE_LEN 8 //!< Length of a BDC Formatted Date #define BCD_TIME_LEN 9 //!< Length of a BDC Formatted Time #define FW_DEBUG_LOG_LEN 128 //!< Length of a firmware debug log chunk via SMBus #define FW_IMG_MAX_CHUNK_SIZE 126 //!< The maximum size of a firmware image chunk #define SECONDS_TO_MICROSECONDS(Seconds) MultU64x32((UINT64)(Seconds), 1000000) #define SECONDS_TO_MICROSECONDS_32(Seconds) Seconds*1000000 #define SECONDS_TO_MILLISECONDS(Seconds) (Seconds * 1000) // PassThru timeout in microseconds #define PT_TIMEOUT_INTERVAL SECONDS_TO_MICROSECONDS(1) // PassThru timeout in milliseconds #define PT_TIMEOUT_INTERVAL_EXT SECONDS_TO_MILLISECONDS(1) // Dcpmm timeout in microseconds #define DCPMM_TIMEOUT_INTERVAL SECONDS_TO_MICROSECONDS_32(1) // Smbus delay period in microseconds #define SMBUS_PT_DELAY_PERIOD_IN_US 100 // Format timeout in microseconds // 120 minutes for 512 GiB dimm #define PT_FORMAT_DIMM_MAX_TIMEOUT SECONDS_TO_MICROSECONDS(7200) #define FW_ABORTED_RETRIES_COUNT_MAX 5 /* * Triggers to modify left shift value - error injection */ #define PACKAGE_SPARING_TRIGGER (1 << 0) #define FATAL_ERROR_TRIGGER (1 << 2) #define SPARE_BLOCK_PERCENTAGE_TRIGGER (1 << 3) #define DIRTY_SHUTDOWN_TRIGGER (1 << 4) // Opt-In Codes - value 0x00 is invalid enum OPT_IN_CODE { NVM_SVN_DOWNGRADE = 0x01, NVM_SECURE_ERASE_POLICY = 0x02, NVM_S3_RESUME = 0x03, NVM_FW_ACTIVATE = 0x04 }; #pragma pack(push) #pragma pack(1) /** Makes a CacheLine durable through the iMC for an Uncacheable region of memory **/ VOID DurableCacheLineWrite( IN VOID *pCacheLineAddress, IN VOID *pRegularBuffer, IN UINT32 NumOfBytes ); /** Copy data from a regular buffer to an interleaved buffer. Using DurableCacheLineWrites Both buffers have to be equal or greater than NumOfBytes. It is expected that the interleaved buffer is at least one cache line between interleaved addresses @param[in] pRegularBuffer input regular buffer @param[out] ppInterleavedBuffer output interleaved buffer @param[in] LineSize line size of interleaved buffer @param[in] NumOfBytes number of bytes to copy **/ VOID DurableCacheLineWriteToInterleavedBuffer( IN VOID *pRegularBuffer, OUT VOID **ppInterleavedBuffer, IN UINT32 LineSize, IN UINT32 NumOfBytes ); /** Clear a part or whole of interleaved buffer. Using DurableCacheLineWrites It is expected that the interleaved buffer is at least one cache line between interleaved addresses @param[out] ppInterleavedBuffer interleaved buffer to clear @param[in] LineSize line size of interleaved buffer @param[in] NumOfBytes number of bytes to clear **/ VOID DurableCacheLineClearInterleavedBuffer( OUT VOID **ppInterleavedBuffer, IN UINT32 LineSize, IN UINT32 NumOfBytes ); /** Pass through command to FW Sends a command to FW and waits for response from firmware @param[in,out] pCmd A firmware command structure @param[in] pMb OPTIONAL A mailbox to call pass through command, if NULL passed then mailbox is taken from inventory base Inventory base may be not initialized on early driver stage. @param[in] Timeout The timeout, in 100ns units, to use for the execution of the protocol command. A Timeout value of 0 means that this function will wait indefinitely for the protocol command to execute. If Timeout is greater than zero, then this function will return EFI_TIMEOUT if the time required to execute the receive data command is greater than Timeout. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid FW Command Parameter @retval EFI_DEVICE_ERROR FW error received @retval EFI_TIMEOUT A timeout occurred while waiting for the protocol command to execute. **/ EFI_STATUS EFIAPI DefaultPassThru ( IN struct _DIMM *pDimm, IN OUT NVM_FW_CMD *pCmd, IN UINT64 Timeout ); /** Pass through command to FW, but retry FW_ABORTED_RETRIES_COUNT_MAX times if we receive a FW_ABORTED response code back. @param[in,out] pCmd A firmware command structure @param[in] pMb OPTIONAL A mailbox to call pass through command, if NULL passed then mailbox is taken from inventory base Inventory base may be not initialized on early driver stage. @param[in] Timeout The timeout, in 100ns units, to use for the execution of the protocol command. A Timeout value of 0 means that this function will wait indefinitely for the protocol command to execute. If Timeout is greater than zero, then this function will return EFI_TIMEOUT if the time required to execute the receive data command is greater than Timeout. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid FW Command Parameter @retval EFI_DEVICE_ERROR FW error received @retval EFI_TIMEOUT A timeout occurred while waiting for the protocol command to execute. **/ EFI_STATUS EFIAPI PassThruWithRetryOnFwAborted( IN struct _DIMM *pDimm, IN OUT NVM_FW_CMD *pCmd, IN UINT64 Timeout ); #ifdef __cplusplus extern "C" { #endif /** Defines the Firmware Command Table opcodes accessed via the EFI_DCPMM_PASS_THRU_PROTOCOL **/ enum PassthroughOpcode { PtIdentifyDimm = 0x01, //!< Retrieve physical inventory data for DIMM PtGetSecInfo = 0x02, //!< Retrieve security information from DIMM PtSetSecInfo = 0x03, //!< Send a security related command to DIMM PtGetFeatures = 0x04, //!< Retrieve modifiable settings for DIMM PtSetFeatures = 0x05, //!< Modify settings for DIMM PtGetAdminFeatures = 0x06, //!< Gets the advanced DIMM settings PtSetAdminFeatures = 0x07, //!< Sets the advanced DIMM settings PtGetLog = 0x08, //!< Retrieve administrative data, error info, other FW data PtUpdateFw = 0x09, //!< Move an image to the DIMM PtInjectError = 0x0A, //!< Validation only CMD to trigger error conditions PtCustomerFormat = 0xF1, //!< DFX command for factory reset PtEmulatedBiosCommands = 0xFD, //!< Perform BIOS emulated command PtMax = 0xFE }; /** Defines the Sub-Opcodes for PtIdentifyDimm **/ enum IdentifyDimmSubop { SubopIdentify = 0x0, SubopDeviceCharacteristics = 0x1 }; /** Defines the Sub-Opcodes for PtGetSecInfo **/ enum GetSecInfoSubop { SubopGetSecState = 0x00, //!< Returns the DIMM security state SubOpGetSecOptIn = 0x02, //!< Returns the DIMM security Opt-In SubOpGetFIPSMode = 0x03 //!< Returns the FIPS Mode }; /** Defines the Sub-Opcodes for PtSetSecInfo **/ enum SetSecInfoSubop { SubopOverwriteDimm = 0x01, SubopSetMasterPass = 0xF0, //!< Changes the security master passphrase SubopSetPass = 0xF1, //!< Changes the security administrator passphrase SubopDisablePass = 0xF2, //!< Disables the current password on a drive SubopUnlockUnit = 0xF3, //!< Unlocks the persistent region SubopReserved = 0xF4, //!< First cmd in erase sequence SubopSecEraseUnit = 0xF5, //!< Second cmd in erase sequence SubopSecFreezeLock = 0xF6 //!< Prevents changes to all security states until reset }; /** Defines the Sub-Opcodes for PtGetFeatures & PtSetFeatures **/ enum GetSetFeatSubop { SubopAlarmThresholds = 0x01, //!< Get/Set alarm threshold data (temperature, spare) SubopPolicyPowMgmt = 0x02, //!< Get various power settings SubopPolicyPackageSparing = 0x03, //!< Get/Set the DIMM Package sparing policy parameters SubopAddressRangeScrub= 0x04, //!< Get/Set Address Range Scrub information and state SubopDDRTAlerts = 0x05, //!< Get what alerts are set to notify the user SubopConfigDataPolicy = 0x06, //!< Get/Set Optional Configuration Data Policy SubopPMONRegisters = 0x07 //!< Get/Set PMON Registers }; /** Defines the Sub-Opcodes for PtGetAdminFeatures & PtSetAdminFeatures **/ enum GetSetAdminFeatSubop { SubopSystemTime = 0x00, //!< Get/Set the internal System Time SubopPlatformDataInfo = 0x01, //!< Get/Set the PCD data SubopDimmPartitionInfo = 0x02, //!< Get the current DIMM partitions configuration SubopFwDbgLogLevel = 0x03, //!< Get/Set the logging level of the internal logger SubopConfigLockdown = 0x05, //!< Get whether or not the lock down has occurred also can disable it SubopDdrtIoInitInfo = 0x06, //!< Get the DDRT initialization info SubopGetSupportedSkuFeatures = 0x07, //!< Get/Set the data regarding supported DIMM SKU capabilities and features SubopLatchSystemShutdownState = 0x09, //!< Get/Set the last system shutdown state data SubopViralPolicy = 0x0A, //!< Get/Set the Viral Policy. SubopCommandAccessPolicy = 0xCA, //!< Get/Set the command access policy SubopExtendedAdr = 0xEA, //!< Get the current extended ADR status of the FW }; /** Defines the Sub-Opcodes for PtGetLog **/ enum GetLogSubop { SubopSmartHealth = 0x00, //!< Retrieves various SMART and Health information SubopFwImageInfo = 0x01, //!< Retrieves information about the FW image SubopFwDbg = 0x02, //!< Retrieves a binary log SubopMemInfo = 0x03, //!< Retrieves memory information regarding the state of the DIMM SubopLongOperationStat = 0x04, //!< Retrieves current status of any long operation in effect SubopErrorLog = 0x05, //!< Retrieves an error log SubopFailureAnalysis = 0xFA, //!< Retrieves the data that can be used for failure analysis. SubopCommandEffectLog = 0xFF //!< Retrieves the Command Effect Log }; /** Defines the Sub-Opcodes for PtUpdateFw **/ enum UpdateFwSubop { SubopUpdateFw = 0x00, //!< Updates the FW Images SubopExecuteFw = 0x01, //!< Executes a new updated FW Image. (without a restart) SubopFwActivate = 0x03 //!< Used to update a successfully downloaded and staged FW without reboot. }; /** Defines the Sub-Opcodes for PtInjectError **/ enum InjectErrorSubop { SubopEnableInjection = 0x00, //!< Allows for errors to be injected SubopErrorPoison = 0x01, //!< Sets poison bit on a DPA SubopMediaErrorTemperature = 0x02, //!< Injects a particular temperature to cause a temperature error SubopSoftwareErrorTriggers = 0x03 //!< SW override triggers to trip various SW alarms }; /** Defines the Sub-Opcodes for PtEmulatedBiosCommands **/ enum PtEmulatedBiosCommandsSubop { SubopGetLPInfo = 0x00, SubopWriteLPInput = 0x01, //!< Returns large payload mailbox information SubopReadLPOutput = 0x02, //!< Copies a buffer to the large payload input mailbox SubopGetBSR = 0x03, //!< Copies the large payload output mailbox to a buffer SubopReserved2 = 0x04, //!< Reserved SubopExtVendorSpecific = 0x05, //!< Performs specified command with user-defined timeout and transport interface }; /** Defines the Transport Interface type for PtExtVendorSpecific **/ enum GetTransportInterface { DdrtTransportInterface = 0x00, SmbusTransportInterface = 0x01, Reserved1 = 0x02, Reserved2 = 0x03 }; /** Payload -> command options -> payload type. **/ #define PCD_CMD_OPT_LARGE_PAYLOAD 0x0 //!< Default #define PCD_CMD_OPT_SMALL_PAYLOAD 0x1 /** Input Payload -> command options -> retrieve type. **/ #define PCD_CMD_OPT_PARTITION_DATA 0x0 //!< Default #define PCD_CMD_OPT_PARTITION_SIZE 0x1 /** Payloads for passthrough fw commands **/ #define FWR_PRODUCT_VERSION_OFFSET 4 #define FWR_REVISION_VERSION_OFFSET 3 #define FWR_SECURITY_VERSION_OFFSET 2 #define FWR_BUILD_VERSION_HI_OFFSET 1 #define FWR_BUILD_VERSION_LOW_OFFSET 0 #define PCD_SET_SMALL_PAYLOAD_DATA_SIZE 64 #define PCD_GET_SMALL_PAYLOAD_DATA_SIZE 128 #define PCD_LARGE_PAYLOAD_DATA_SIZE 0x1000 #define SMALL_PAYLOAD_SIZE 128 /** Memory Page options for Memory Info Input Payload **/ #define MEMORY_INFO_PAGE_0 0X0 #define MEMORY_INFO_PAGE_1 0X1 #define MEMORY_INFO_PAGE_3 0X3 #define MEMORY_INFO_PAGE_4 0X4 /** MemInfo page 3 Error Inject status bits **/ #define ERR_INJECTION_ENABLED_BIT 0x0 #define ERR_INJECTION_ENABLED_BIT_MASK 0x1 #define ERR_INJECTION_MEDIA_TEMP_ENABLED_BIT 0x1 #define ERR_INJECTION_MEDIA_TEMP_ENABLED_BIT_MASK 0x1 #define ERR_INJECTION_SW_TRIGGER_ENABLED_BIT 0x2 #define ERR_INJECTION_SW_TRIGGER_ENABLED_BIT_MASK 0x1 /** Passthrough Payload: Opcode: 0x01h (Identify DIMM) **/ typedef struct { UINT16 Vid; //!< 1-0 : DIMM vendor id UINT16 Did; //!< 3-2 : Device ID UINT16 Rid; //!< 5-4 : Revision ID UINT16 Ifc; //!< 7-6 : Interface format code (0x301) UINT8 Fwr[FW_BCD_VERSION_LEN]; //!< 12-8 : BCD formatted firmware revision UINT8 Reserved0; //!< 13 : Reserved UINT8 Fswr; //!< 14 : Feature SW Required Mask UINT8 Reserved1; //!< 15 : Reserved UINT8 Reserved2[16]; //!< 31-16 : Reserved UINT32 Rc; //!< 35-32 : Raw capacity UINT16 Mf; //!< 37-36 : Manufacturer ID (Deprecated) UINT32 Sn; //!< 41-38 : Serial Number ID (Deprecated) CHAR8 Pn[DIMM_PN_LEN]; //!< 61-42 : ASCII Part Number UINT32 DimmSku; //!< 65-62 : DIMM SKU UINT8 Reserved3[2]; //!< 66-67 : Reserved UINT16 ApiVer; //!< 69-68 : API Version UINT8 DimmUid[9]; //!< 78-70 : DIMM Unique ID (UID) UINT16 ActiveApiVer; //!< 80-79 : Active API UINT8 Reserved4[47]; //!< 127-81: Reserved } PT_ID_DIMM_PAYLOAD; typedef struct { TEMPERATURE ControllerShutdownThreshold; TEMPERATURE MediaShutdownThreshold; TEMPERATURE MediaThrottlingStartThreshold; TEMPERATURE MediaThrottlingStopThreshold; TEMPERATURE ControllerThrottlingStartThreshold; TEMPERATURE ControllerThrottlingStopThreshold; UINT16 MaxAveragePowerLimit; UINT8 Reserved[114]; } PT_DEVICE_CHARACTERISTICS_PAYLOAD; typedef struct { TEMPERATURE ControllerShutdownThreshold; TEMPERATURE MediaShutdownThreshold; TEMPERATURE MediaThrottlingStartThreshold; TEMPERATURE MediaThrottlingStopThreshold; TEMPERATURE ControllerThrottlingStartThreshold; TEMPERATURE ControllerThrottlingStopThreshold; UINT16 MaxAveragePowerLimit; UINT16 MaxMemoryBandwidthBoostMaxPowerLimit; UINT32 MaxMemoryBandwidthBoostAveragePowerTimeConstant; UINT32 MemoryBandwidthBoostAveragePowerTimeConstantStep; UINT32 MaxAveragePowerReportingTimeConstant; UINT32 AveragePowerReportingTimeConstantStep; UINT8 Reserved[96]; } PT_DEVICE_CHARACTERISTICS_PAYLOAD_2_1; typedef struct { UINT8 FisMajor; UINT8 FisMinor; union { PT_DEVICE_CHARACTERISTICS_PAYLOAD Fis_1_15; PT_DEVICE_CHARACTERISTICS_PAYLOAD_2_1 Fis_2_01; UINT8 Data[0]; }Payload; }PT_DEVICE_CHARACTERISTICS_OUT; /** Passthrough Payload: Opcode: 0x06h (Get Admin Features) Sub-Opcode: 0x02h (DIMM Partition Info) **/ typedef struct { UINT32 VolatileCapacity; UINT32 Reserved; UINT64 VolatileStart; UINT32 PersistentCapacity; UINT32 Reserved2; UINT64 PersistentStart; UINT32 RawCapacity; UINT8 Reserved3[92]; } PT_DIMM_PARTITION_INFO_PAYLOAD; /** Passthrough Payload: Opcode: 0x06h (Get Admin Features) Sub-Opcode: 0x04h (Persistent Partition) **/ typedef struct { // Bit 0: Partition Enabled, Bit 1: Viral Policy Enabled UINT8 State; UINT8 Reserved[127]; } PT_DIMM_PARTITION_STATE_PAYLOAD; /** Passthrough Payload: Opcode: 0x02h (Get Security Info) Sub-Opcode: 0x00h (Get Security State) **/ typedef struct { union { struct { UINT32 Reserved1 : 1; UINT32 SecurityEnabled : 1; UINT32 SecurityLocked : 1; UINT32 SecurityFrozen : 1; UINT32 UserSecurityCountExpired : 1; UINT32 SecurityNotSupported : 1; //!< This SKU does not support Security Feature Set UINT32 BIOSSecurityNonceSet : 1; UINT32 Reserved2 : 1; UINT32 MasterPassphraseEnabled : 1; UINT32 MasterSecurityCountExpired : 1; UINT32 Reserved3 : 22; } Separated; UINT32 AsUint32; } SecurityStatus; union { struct { UINT32 SecurityErasePolicy : 1; //!< 0 - Never been set, 1 - Secure Erase Policy opted in UINT32 Reserved :31; } Separated; UINT32 AsUint32; } OptInStatus; UINT8 Reserved[120]; } PT_GET_SECURITY_PAYLOAD; /** Passthrough Input Payload: Opcode: 0x02h (Get Security Info) Sub-Opcode: 0x02h (Get Security Opt-In) **/ typedef struct { UINT16 OptInCode; UINT8 Reserved[126]; } PT_INPUT_PAYLOAD_GET_SECURITY_OPT_IN; /** Passthrough Output Payload: Opcode: 0x02h (Get Security Info) Sub-Opcode: 0x02h (Get Security Opt-In) **/ typedef struct { UINT16 OptInCode; UINT8 Reserved[2]; UINT32 OptInValue; UINT8 OptInModify; UINT8 Reserved2[3]; UINT8 OptInWindow; UINT8 Reserved3[51]; UINT8 OptInSpecificData[64]; } PT_OUTPUT_PAYLOAD_GET_SECURITY_OPT_IN; /** Passthrough Payload: Opcode: 0x02h (Set Security Info) Sub-Opcode: 0xF1h (Set Passphrase) **/ typedef struct { UINT8 PassphraseCurrent[PASSPHRASE_BUFFER_SIZE]; //!< 31:0 The current security passphrase UINT8 PassphraseType; //!< 32 Passphrase Type for secure erase UINT8 Reserved1[31]; //!< 63:33 Reserved UINT8 PassphraseNew[PASSPHRASE_BUFFER_SIZE]; //!< 64:95 The new passphrase to be set/changed to UINT8 Reserved2[32]; //!< 127:96 Reserved } PT_SET_SECURITY_PAYLOAD; typedef struct { UINT8 Reserved1[4]; //!< 3:0 Reserved UINT32 AveragePowerReportingTimeConstant; //!< 7:4 Average Power Reporting Time Constant UINT8 Reserved2[120]; //!< 127:4 Reserved } PT_OPTIONAL_DATA_POLICY_PAYLOAD_2_1; typedef struct { UINT8 FisMajor; UINT8 FisMinor; union { PT_OPTIONAL_DATA_POLICY_PAYLOAD_2_1 Fis_2_01; UINT8 Data[0]; }Payload; } PT_OPTIONAL_DATA_POLICY_PAYLOAD; /** Passthrough Payload: Opcode: 0x04h (Get Features) Sub-Opcode: 0x01h (Alarm Thresholds) **/ typedef struct { /** Enable/Disable alarms. **/ union { UINT16 AllBits; struct { UINT16 PercentageRemaining : 1; UINT16 MediaTemperature : 1; UINT16 ControllerTemperature : 1; UINT16 : 13; //!< Reserved } Separated; } Enable; /** When spare levels fall below this percentage based value, asynchronous events may be triggered and may cause a transition in the overall health state **/ UINT8 PercentageRemainingThreshold; /** Media temperature threshold (in Celsius) above this threshold trigger asynchronous events and may cause a transition in the overall health state **/ TEMPERATURE MediaTemperatureThreshold; /** Controller temperature threshold (in Celsius) above this threshold trigger asynchronous events and may cause a transition in the overall health state **/ TEMPERATURE ControllerTemperatureThreshold; UINT8 Reserved[121]; } PT_PAYLOAD_ALARM_THRESHOLDS; /** Passthrough Payload: Opcode: 0x04h (Get Features) Sub-Opcode: 0x02h (Power Management Policy) **/ typedef struct { UINT8 Reserved1; /** Power budget in mW used for instantaneous power. Valid range for power budget 10000 - 20000 mW. **/ UINT16 PeakPowerBudget; /** Power budget in mW used for averaged power. Valid range for power budget 10000 - 18000 mW. **/ UINT16 AveragePowerLimit; UINT8 Reserved2[123]; } PT_PAYLOAD_POWER_MANAGEMENT_POLICY; typedef struct { UINT8 Reserved1[3]; /** Power limit in mW used for averaged power. Valid range for power limit 10000 - 18000 mW. **/ UINT16 AveragePowerLimit; UINT8 Reserved2; /** Returns if the Memory Bandwidth Boost Mode is currently enabled or not. **/ UINT8 MemoryBandwidthBoostFeature; /** Power limit [mW] used for limiting the Memory Bandwidth Boost Mode power consumption. Valid range for Memory Bandwidth Boost Power Limit starts from 15000 - X mW, where X represents the value returned from Get Device Characteristics command's Max Memory Bandwidth Boost Max Power Limit field. **/ UINT16 MemoryBandwidthBoostMaxPowerLimit; /** The value used as a base time window for power usage measurements [ms]. **/ UINT32 MemoryBandwidthBoostAveragePowerTimeConstant; UINT8 Reserved3[115]; } PT_PAYLOAD_POWER_MANAGEMENT_POLICY_2_1; typedef struct { UINT8 FisMajor; UINT8 FisMinor; union { PT_PAYLOAD_POWER_MANAGEMENT_POLICY Fis_1_15; PT_PAYLOAD_POWER_MANAGEMENT_POLICY_2_1 Fis_2_01; UINT8 Data[0]; }Payload; } PT_POWER_MANAGEMENT_POLICY_OUT; typedef struct { UINT8 PayloadType : 1; UINT8 RetrieveOption : 1; UINT8 Reserved : 6; } PT_INPUT_PAYLOAD_COMMAND_OPTIONS; /** Passthrough Payload: Opcode: 0x06h (Get Admin Features) Sub-Opcode: 0x01h (Platform Config Data) **/ typedef struct { /** PartitionId possible values: 0x00 - 1st Partition - Interleave configurations - for BIOS usage only 0x01 - 2nd Partition - Interleave configurations - for OEM and OS usage 0x02 - 3rd Partition - Namespace Label Storage Area **/ UINT8 PartitionId; //!< 0 : PT_INPUT_PAYLOAD_COMMAND_OPTIONS CmdOptions; //!< 1 : Additional options passed UINT32 Offset; //!< 5-2 : (SmallPayload only) Offset in bytes of partition to start reading from UINT8 Reserved[122]; //!< 127-6 : } PT_INPUT_PAYLOAD_GET_PLATFORM_CONFIG_DATA; /** Passthrough Payload: Opcode: 0x06h (Get Admin Features) Sub-Opcode: 0x01h (Platform Config Data) **/ typedef struct { UINT32 Size; UINT8 Reserved[124]; } PT_OUTPUT_PAYLOAD_GET_PLATFORM_CONFIG_DATA_SIZE; /** Passthrough Payload: Opcode: 0x06h (Get Admin Features) Sub-Opcode: 0x05h (FW Debug Log Level) **/ typedef struct { /** The current logging level of the FW (0-255). 0 = Disabled 1 = Error 2 = Warning 3 = Info 4 = Debug **/ UINT8 LogLevel; //!< 0 Log Level UINT8 LogsCount; //!< 1 Number of logs to retrieve } PT_OUTPUT_PAYLOAD_FW_DEBUG_LOG_LEVEL; /** This struct holds information about DIMM capabilities and features **/ typedef struct _SKU_INFORMATION { UINT32 MemoryModeEnabled : 1; UINT32 : 1; //!< Reserved UINT32 AppDirectModeEnabled : 1; UINT32 PackageSparingCapable : 1; UINT32 : 12; //!< Reserved UINT32 SoftProgrammableSku : 1; UINT32 EncryptionEnabled : 1; UINT32 : 14; //!< Reserved } SKU_INFORMATION; /** Passthrough Payload: Opcode: 0x06h (Get Admin Features) Sub-Opcode: 0x0Ah (Viral Policy) **/ typedef struct { UINT8 ViralPolicyEnable; //!< Viral Policy Enable: 0 - Disabled, 1 - Enabled UINT8 ViralStatus; //!< Viral Status: 0 - Not Viral, 1 - Viral UINT8 Reserved[126]; //!< Reserved } PT_VIRAL_POLICY_PAYLOAD; /** Passthrough Payload: Opcode: 0x07h (Set Admin Features) Sub-Opcode: 0x01h (Platform Config Data) **/ typedef struct { /** 0x00 - 1st Partition - Interleave configurations - for BIOS usage only 0x01 - 2nd Partition - Interleave configurations - for OEM and OS usage 0x02 - 3rd Partition - Namespace Label Storage Area **/ UINT8 PartitionId; //!< 0 : Which partition to access UINT8 PayloadType; //!< 1 : Large or small payload UINT32 Offset; //!< 5-2 : Offset in bytes of partition to start reading from UINT8 Reserved[58]; //!< 63-6 : Reserved UINT8 Data[64]; //!< 127-64 : } PT_INPUT_PAYLOAD_SET_DATA_PLATFORM_CONFIG_DATA; /** Passthrough Payload: Opcode: 0x04h (Get Features) Sub-Opcode: 0x03h (Package Sparing Policy) **/ typedef struct { UINT8 Enable; //!< Reflects whether the package sparing policy is enabled or disabled (0x00 = Disabled). UINT8 Reserved1; //!< Reserved UINT8 Supported; //!< Designates whether or not the DIMM still supports package sparing. UINT8 Reserved[125]; //!< 127-3 : Reserved } PT_PAYLOAD_GET_PACKAGE_SPARING_POLICY; /** Passthrough Payload: Opcode: 0x05h (Set Features) Sub-Opcode: 0x03h (Package Sparing Policy) **/ typedef struct { UINT8 Enable; //!< Reflects whether the package sparing policy is enabled or disabled (0x00 = Disabled). UINT8 Reserved[127]; //!< 127-1 : Reserved } PT_PAYLOAD_SET_PACKAGE_SPARING_POLICY; /** Passthrough Payload: Opcode: 0x04h (Get Features) Sub-Opcode: 0x04h (Address Range Scrub) **/ typedef struct { UINT8 Enable; //!< Indicates whether an Address Range Scrub is in progress. UINT8 Reserved1[3]; UINT64 DPAStartAddress; //!< Address from which to start the range scrub. UINT64 DPAEndAddress; //!< Address to end the range scrub. UINT64 DPACurrentAddress; //!< Address that is being currently scrubbed. UINT8 Reserved2[100]; } PT_PAYLOAD_ADDRESS_RANGE_SCRUB; /** Passthrough Payload: Opcode: 0x04h (Get Features) Sub-Opcode: 0x04h (Address Range Scrub) **/ typedef struct { UINT8 Enable; //!< Indicates whether an Address Range Scrub is in progress. UINT8 Reserved1[3]; UINT64 DPAStartAddress; //!< Address from which to start the range scrub. UINT64 DPAEndAddress; //!< Address to end the range scrub. UINT8 Reserved2[108]; } PT_PAYLOAD_SET_ADDRESS_RANGE_SCRUB; typedef union _SMART_VALIDATION_FLAGS { UINT32 AllFlags; struct { UINT32 HealthStatus : 1; UINT32 PercentageRemaining : 1; UINT32 : 1; //!< Reserved UINT32 MediaTemperature : 1; UINT32 ControllerTemperature : 1; UINT32 LatchedDirtyShutdownCount : 1; UINT32 AITDRAMStatus : 1; UINT32 HealthStatusReason : 1; UINT32 : 1; //!< Reserved UINT32 AlarmTrips : 1; UINT32 LatchedLastShutdownStatus : 1; UINT32 SizeOfVendorSpecificDataValid : 1; UINT32 : 20; //!< Reserved } Separated; } SMART_VALIDATION_FLAGS; typedef struct { UINT64 PowerCycles; //!< Number of DIMM power cycles UINT64 PowerOnTime; //!< Lifetime hours the DIMM has been powered on (represented in seconds) UINT64 UpTime; //!< Current uptime of the DIMM for the current power cycle UINT32 UnlatchedDirtyShutdownCount; //!< This is the # of times that the FW received an unexpected power loss /** Display the status of the last shutdown that occurred Bit 0: PM ADR Command (0 - Not Received, 1 - Received) Bit 1: PM S3 (0 - Not Received, 1 - Received) Bit 2: PM S5 (0 - Not Received, 1 - Received) Bit 3: DDRT Power Fail Command Received (0 - Not Received, 1 - Received) Bit 4: PMIC Power Loss (0 - Not Received, 1 - PMIC Power Loss) Bit 5: PM Warm Reset (0 - Not Received, 1 - Received) Bit 6: Thermal Shutdown Received (0 - Did not occur, 1 Thermal Shutdown Triggered) Bit 7: Controller Flush Complete (0 - Did not occur, 1 - Completed) **/ LAST_SHUTDOWN_STATUS_DETAILS LatchedLastShutdownDetails; UINT64 LastShutdownTime; /** Display extended details of the last shutdown that occurred Bit 0: Viral Interrupt Command (0 - Not Received, 1 - Received) Bit 1: Surprise Clock Stop Interrupt (0 - Not Received, 1 - Received) Bit 2: Write Data Flush Complete (0 - Not Completed, 1 - Completed) Bit 3: S4 Power State (0 - Not Received, 1 - Received) Bit 4: PM Idle or SRE Clock Stop (0 - Not Received, 1 - Received) Bit 5: Surprise Reset (0 - Not Received, 1 - Received) Bit 6-23: Reserved **/ LAST_SHUTDOWN_STATUS_DETAILS_EXTENDED LatchedLastShutdownExtendedDetails; UINT8 Reserved[2]; /** Display the status of the last shutdown that occurred Bit 0: PM ADR Command (0 - Not Received, 1 - Received) Bit 1: PM S3 (0 - Not Received, 1 - Received) Bit 2: PM S5 (0 - Not Received, 1 - Received) Bit 3: DDRT Power Fail Command Received (0 - Not Received, 1 - Received) Bit 4: PMIC Power Loss (0 - Not Received, 1 - PMIC Power Loss) Bit 5: PM Warm Reset (0 - Not Received, 1 - Received) Bit 6: Thermal Shutdown Received (0 - Did not occur, 1 Thermal Shutdown Triggered) Bit 7: Controller Flush Complete (0 - Did not occur, 1 - Completed) **/ LAST_SHUTDOWN_STATUS_DETAILS UnlatchedLastShutdownDetails; /** Display extended details of the last shutdown that occurred Bit 0: Viral Interrupt Command (0 - Not Received, 1 - Received) Bit 1: Surprise Clock Stop Interrupt (0 - Not Received, 1 - Received) Bit 2: Write Data Flush Complete (0 - Not Completed, 1 - Completed) Bit 3: S4 Power State (0 - Not Received, 1 - Received) Bit 4: PM Idle or SRE Clock Stop (0 - Not Received, 1 - Received) Bit 5: Surprise Reset (0 - Not Received, 1 - Received) Bit 6-23: Reserved **/ LAST_SHUTDOWN_STATUS_DETAILS_EXTENDED UnlatchedLastShutdownExtendedDetails; TEMPERATURE MaxMediaTemperature; //!< The highest die temperature reported in degrees Celsius. TEMPERATURE MaxControllerTemperature; //!< The highest controller temperature reported in degrees Celsius. UINT8 ThermalThrottlePerformanceLossPercent; //!< The average loss % due to thermal throttling since last read in current boot UINT8 Reserved1[41]; } SMART_INTEL_SPECIFIC_DATA; /** Passthrough Payload: Opcode: 0x08h (Get Log Page) Sub-Opcode: 0x00h (SMART & Health Info) FIS 1.9 **/ typedef struct { /** If validation flag is not set it indicates that corresponding field is not valid**/ SMART_VALIDATION_FLAGS ValidationFlags; UINT32 Reserved; /** Overall health summary Bit 0: Normal (no issues detected) Bit 1: Noncritical (maintenance required) Bit 2: Critical (features or performance degraded due to failure) Bit 3: Fatal (data loss has occurred or is imminent) Bits 7-4 Reserved **/ UINT8 HealthStatus; UINT8 PercentageRemaining; //!< remaining percentage remaining as a percentage of factory configured spare UINT8 Reserved2; /** Bits to signify whether or not values has tripped their respective thresholds. Bit 0: Spare Blocks trips (0 - not tripped, 1 - tripped) Bit 1: Media Temperature trip (0 - not tripped, 1 - tripped) Bit 2: Controller Temperature trip (0 - not tripped, 1 - tripped) **/ union { UINT8 AllFlags; struct { UINT8 PercentageRemaining : 1; UINT8 MediaTemperature : 1; UINT8 ControllerTemperature : 1; } Separated; } AlarmTrips; TEMPERATURE MediaTemperature; //!< Current temperature in Celsius. This is the highest die temperature reported. TEMPERATURE ControllerTemperature; //!< Current temperature in Celsius. This is the temperature of the controller. UINT32 LatchedDirtyShutdownCount; //!< Number of times the DIMM Last Shutdown State (LSS) was non-zero. UINT8 AITDRAMStatus; //!< The current state of the AIT DRAM (0 - failure occurred, 1 - loaded) UINT16 HealthStatusReason; //!< Indicates why the module is in the current Health State UINT8 Reserved3[8]; /** 00h: Clean Shutdown 01h - FFh: Not Clean Shutdown **/ UINT8 LatchedLastShutdownStatus; UINT32 VendorSpecificDataSize; //!< Size of Vendor specific structure SMART_INTEL_SPECIFIC_DATA VendorSpecificData; } PT_PAYLOAD_SMART_AND_HEALTH; /** Passthrough Payload - Get Log Page - Firmware Image Info **/ typedef struct { UINT8 FwRevision[FW_BCD_VERSION_LEN]; UINT8 Reserved1; UINT16 FWImageMaxSize; UINT8 Reserved2[8]; UINT8 StagedFwRevision[FW_BCD_VERSION_LEN]; UINT8 StagedFwActivatable; UINT8 LastFwUpdateStatus; UINT8 QuiesceRequired; UINT16 ActivationTime; UINT8 Reserved4[102]; } PT_PAYLOAD_FW_IMAGE_INFO; #define SRAM_LOG_PAGE_SIZE_BYTES KIB_TO_BYTES(2) #define SPI_LOG_PAGE_SIZE_BYTES KIB_TO_BYTES(2) enum GetFWDebugLogLogAction { ActionRetrieveDbgLogSize = 0x00, ActionGetDbgLogPage = 0x01, ActionGetSramLogPage = 0x02, ActionGetSpiLogPage = 0x03, ActionInvalid = 0x04, }; /** Passthrough Payload: Opcode: 0x08h (Get Log Page) Sub-Opcode: 0x02h (Firmware Debug Log) **/ typedef struct { UINT8 LogAction; UINT32 LogPageOffset; UINT8 PayloadType; UINT8 Reserved[122]; } PT_INPUT_PAYLOAD_FW_DEBUG_LOG; typedef struct { UINT8 LogSize; //!< Log size in MB UINT8 Reserved[127]; } PT_OUTPUT_PAYLOAD_FW_DEBUG_LOG; /** Passthrough Input Payload: Opcode: 0x08h (Get Log Page) Sub-Opcode: 0x03h (Memory Info) **/ typedef struct { UINT8 MemoryPage; //!< Page of the memory information to retrieve UINT8 Reserved[127]; } PT_INPUT_PAYLOAD_MEMORY_INFO; /** Passthrough Output Payload: Opcode: 0x08h (Get Log Page) Sub-Opcode: 0x03h (Memory Info) Page: 0 (Current Boot Info) **/ typedef struct { UINT128 MediaReads; //!< Number of 64 byte reads from media on the DCPMM since last AC cycle UINT128 MediaWrites; //!< Number of 64 byte writes to media on the DCPMM since last AC cycle UINT128 ReadRequests; //!< Number of DDRT read transactions the DCPMM has serviced since last AC cycle UINT128 WriteRequests; //!< Number of DDRT write transactions the DCPMM has serviced since last AC cycle UINT8 Reserved[64]; //!< Reserved } PT_OUTPUT_PAYLOAD_MEMORY_INFO_PAGE0; /** Passthrough Output Payload: Opcode: 0x08h (Get Log Page) Sub-Opcode: 0x03h (Memory Info) Page: 1 (Lifetime Info) **/ typedef struct { UINT128 TotalMediaReads; //!< Lifetime number of 64 byte reads from media on the DCPMM UINT128 TotalMediaWrites; //!< Lifetime number of 64 byte writes to media on the DCPMM UINT128 TotalReadRequests; //!< Lifetime number of DDRT read transactions the DCPMM has serviced UINT128 TotalWriteRequests; //!< Lifetime number of DDRT write transactions the DCPMM has serviced UINT8 Reserved[64]; //!< Reserved } PT_OUTPUT_PAYLOAD_MEMORY_INFO_PAGE1; /** Passthrough Output Payload: Opcode: 0x08h (Get Log Page) Sub-Opcode: 0x03h (Memory Info) Page: 3 (Error Injection Statistics) **/ typedef struct { /** This bit field specifies the error inject state: * 0 - Error injection enabled * 1 - Media temperature injection is enabled * 2 - At least one software trigger is enabled * 31:2 - reserved **/ UINT32 ErrorInjectStatus; UINT32 PoisonErrorInjectionsCounter; //!< This counter will be incremented each time the set poison error is successfully executed UINT32 PoisonErrorClearCounter; //!< This counter will be incremented each time the clear poison error is successfully executed UINT32 MediaTemperatureInjectionsCounter; //!< This counter will be incremented each time the media temperature is injected UINT32 SoftwareTriggersCounter; //!< This counter is incremented each time a software trigger is enabled UINT64 SoftwareTriggersEnabledDetails; //!< For each bit set, the corresponding trigger is currently enabled. UINT8 Reserved[100]; //!< Reserved } PT_OUTPUT_PAYLOAD_MEMORY_INFO_PAGE3; /** Passthrough Output Payload: Opcode: 0x08h (Get Log Page) Sub-Opcode: 0x03h (Memory Info) Page: 4 (Average Power Consumption Statistics) **/ typedef struct { UINT16 DcpmmAveragePower; //!< Average power consumption by the module UINT16 AveragePower12V; //!< 12V average power consumption by the module UINT16 AveragePower1_2V; //!< 1.2V average power consumption by the module UINT8 Reserved[122]; //!< Reserved } PT_OUTPUT_PAYLOAD_MEMORY_INFO_PAGE4; /** Passthrough Output Payload: Opcode: 0x08h (Get Log Page) Sub-Opcode: 0x04h (Long Operation Status) **/ typedef struct { UINT8 CmdOpcode; UINT8 CmdSubOpcode; UINT16 Percent; UINT32 EstimatedTimeLeft; UINT8 Status; UINT8 CmdSpecificData[119]; } PT_OUTPUT_PAYLOAD_FW_LONG_OP_STATUS; enum GetErrorLogLevel { ErrorLogLowPriority = 0x00, ErrorLogHighPriority = 0x01, ErrorLogInvalidPriority = 0x02, }; enum GetErrorLogType { ErrorLogTypeMedia = 0x00, ErrorLogTypeThermal = 0x01, ErrorLogTypeInvalid = 0x02, }; enum GetErrorLogInfo { ErrorLogInfoEntries = 0x00, ErrorLogInfoData = 0x01, ErrorLogInfoInvalid = 0x02, }; enum GetErrorLogPayloadReturn { ErrorLogSmallPayload = 0x00, ErrorLogLargePayload = 0x01, ErrorLogInvalidPayload = 0x02, }; /** Transaction type that caused error. Limited to 64 transaction types **/ enum GetErrorTransactionType { ErrorTransaction2LMRead = 0x00, ErrorTransaction2LMWrite = 0x01, ErrorTransactionPMRead = 0x02, ErrorTransactionPMWrite = 0x03, ErrorTransactionBWRead = 0x04, ErrorTransactionBWWrite = 0x05, ErrorTransactionAITRead = 0x06, ErrorTransactionAITWrite = 0x07, ErrorTransactionWearLevelMove = 0x08, ErrorTransactionPatrolScrub = 0x09, ErrorTransactionCSRRead = 0x0A, ErrorTransactionCSRWrite = 0x0B, /** 0x0C - 0x40 Reserved for other transaction **/ ErrorTransactionNotValid = 0x41, /** 0x41 - 0xFF Not Valid **/ }; /** Temperature group being reported. **/ enum GetErrorThermalReportedType { ErrorThermalReportedLow = 0x1, ErrorThermalReportedHigh = 0x2, ErrorThermalReportedCritical = 0x4 }; /** Mailbox status return codes. **/ enum MailboxStatusReturnCode { MailboxSuccess = FW_SUCCESS, MailboxDeviceBusy = FW_DEVICE_BUSY, MailboxDataNotSet = FW_DATA_NOT_SET }; /** Passthrough Input Payload: Opcode: 0x08h (Get Log Page) Sub-Opcode: 0x05h (Error Log) **/ typedef struct { union { UINT8 AsUint8; struct { UINT8 LogLevel : 1; //!< Specifies which error log to retrieve UINT8 LogType : 1; //!< Specifies which log type to access UINT8 LogInfo : 1; //!< Specifies which log type data to return (entries / log info) UINT8 LogEntriesPayloadReturn : 1; //!< Specifies which payload return log entries (small / large) UINT8 : 4; //!< Reserved } Separated; } LogParameters; UINT16 SequenceNumber; //!< Log entries with sequence number equal or higher than the provided will be returned UINT16 RequestCount; //!< Max number of log entries requested for this access UINT8 Reserved[123]; } PT_INPUT_PAYLOAD_GET_ERROR_LOG; typedef struct _LOG_INFO_DATA_RETURN { UINT16 MaxLogEntries; UINT16 CurrentSequenceNum; UINT16 OldestSequenceNum; UINT64 OldestLogEntryTimestamp; UINT64 NewestLogEntryTimestamp; UINT8 AdditionalLogStatus; UINT8 Reserved[105]; } LOG_INFO_DATA_RETURN; /** Passthrough Output Payload (Media): Opcode: 0x08h (Get Log Page) Sub-Opcode: 0x05h (Error Log) **/ typedef struct { UINT16 ReturnCount; //!< Number of log entries returned UINT8 LogEntries[126]; //!< Media log entry table } PT_OUTPUT_PAYLOAD_GET_ERROR_LOG; /** Passthrough Output Media Log Entry Format **/ typedef struct { UINT64 SystemTimestamp; //!< Unix epoch time of log entry UINT64 Dpa; //!< Specifies DPA address of error UINT64 Pda; //!< Specifies PDA address of the failure UINT8 Range; //!< Specifies the length in address space of this error. Ranges will be encoded as power of 2. UINT8 ErrorType; //!< Indicates what kind of error was logged. union { UINT8 AsUint8; //!< Indicates error flags for this entry. struct { UINT8 PdaValid : 1; //!< Indicates the PDA address is valid. UINT8 DpaValid : 1; //!< Indicates the DPA address is valid. UINT8 Interrupt : 1; //!< Indicates this error generated an interrupt packet UINT8 : 1; //!< Reserved UINT8 Viral : 1; //!< Indicates Viral was signaled for this error UINT8 : 3; //!< Reserved } Separated; } ErrorFlags; UINT8 TransactionType; //!< Indicates what transaction caused the error UINT16 SequenceNum; UINT8 Reserved[2]; } PT_OUTPUT_PAYLOAD_GET_ERROR_LOG_MEDIA_ENTRY; /** Passthrough Output Thermal Log Entry Format **/ typedef struct { UINT64 SystemTimestamp; //!< Unix epoch time of log entry union { UINT32 AsUint32; struct { UINT32 Temperature : 15; //!< In celsius UINT32 Sign : 1; //!< Positive or negative UINT32 Reported : 3; //!< Temperature being reported UINT32 Type : 2; //!< Controller or media temperature UINT32 : 11; //!< Reserved } Separated; } HostReportedTempData; UINT16 SequenceNum; UINT8 Reserved[2]; } PT_OUTPUT_PAYLOAD_GET_ERROR_LOG_THERMAL_ENTRY; /** Passthrough Input Payload: Opcode: 0x08h (Get Log Page) Sub-Opcode: 0xFFh (Command Effect Log) **/ typedef struct { UINT8 PayloadType; UINT8 LogAction; UINT8 EntryOffset; UINT8 Reserved[125]; } PT_INPUT_PAYLOAD_GET_COMMAND_EFFECT_LOG; /** Get Command Effect Log Input Payload Enum types **/ enum GetCelPayloadType { LargePayload = 0x00, SmallPayload = 0x01 }; enum GetCelLogAction { EntriesCount = 0x00, CelEntries = 0x01 }; /** Passthrough Output Payload: Opcode: 0x08h (Get Log Page) Sub-Opcode: 0xFFh (Command Effect Log) **/ typedef struct { UINT32 LogEntryCount; UINT8 Reserved[124]; } PT_OUTPUT_PAYLOAD_GET_CEL_COUNT; typedef struct { COMMAND_EFFECT_LOG_ENTRY CelEntry[16]; } PT_OUTPUT_PAYLOAD_GET_CEL_ENTRIES; typedef struct { union { PT_OUTPUT_PAYLOAD_GET_CEL_COUNT CelCount; PT_OUTPUT_PAYLOAD_GET_CEL_ENTRIES CelEntries; UINT8 Data[0]; } LogTypeData; } PT_OUTPUT_PAYLOAD_GET_COMMAND_EFFECT_LOG; /** Passthrough Input Payload: Opcode: 0x07h (Get Admin Feature) Sub-Opcode: 0xCAh (Command Access Policy) **/ typedef struct { UINT8 Opcode; UINT8 Subopcode; UINT8 Reserved2[126]; } PT_INPUT_PAYLOAD_GET_COMMAND_ACCESS_POLICY; typedef struct { UINT8 Restriction; UINT8 Reserved[127]; } PT_OUTPUT_PAYLOAD_GET_COMMAND_ACCESS_POLICY; /** Passthrough Output DDRT IO Init Info **/ typedef struct { union { UINT8 AsUint8; struct { /** Bit 0-3: Valid Values: 0b0000 = 1600 MT/s (default) 0b0001 = 1866 MT/s 0b0010 = 2133 MT/s 0b0011 = 2400 MT/s 0b0100 = 2666 MT/s 0b0101 = 2933 MT/s 0b0110 = 3200 MT/s 0b0111 = reserved 0b1xxx = reserved Bit 4: VDDQ (0 - 1.2V (default), 1 - reserved for low voltage) Bit 5: Write Preamble (0 - 1 nCk (default), 1 - 2 nCk) Bit 6: Read Preamble (0 - 1 nCk (default), 1 - 2 nCk) Bit 7: Gate PLL (0 - PLLs Un-Gated, 1 - PLLs Gated) **/ UINT8 OperatingFrequency : 4; //!< Valid values above. UINT8 Vddq : 1; //!< Encoding for DDRT voltage UINT8 WritePreamble : 1; //!< DDRT Mode Register for Write Preamble. UINT8 ReadPreamble : 1; //!< DDRT Mode Register for Read Preamble. UINT8 GatePll : 1; //!< This denotes whether the FW is gating PLLs for programming. } Separated; } DdrtIoInfo; /** DDRT Training State possible values: 0x00 - Training Not Complete 0x01 - Training Complete 0x02 - Training Failure 0x03 - S3 Complete 0x04 - Normal Mode Complete **/ UINT8 DdrtTrainingStatus; //! #include #include "NvmDimmDriverData.h" #include "NvmDimmPassThru.h" #include "NvmSecurity.h" #include "Namespace.h" #include "Debug.h" #include "Dimm.h" extern NVMDIMMDRIVER_DATA *gNvmDimmData; /** Get DIMM security state @param [in] DimmPid Pointer to DIMM @param [in] Timeout The timeout in 100ns units, to use for the PassThru protocol @param [out] pSecurityState DIMM security state @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_DEVICE_ERROR Failed on PassThru protocol @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI GetDimmSecurityState( IN DIMM *pDimm, IN UINT64 Timeout, OUT UINT32 *pSecurityState ) { NVM_FW_CMD *pPassThruCommand = NULL; PT_GET_SECURITY_PAYLOAD *pSecurityPayload = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pSecurityState == NULL){ goto Finish; } pPassThruCommand = AllocateZeroPool(sizeof(*pPassThruCommand)); if (pPassThruCommand == NULL) { NVDIMM_ERR("Out of memory."); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pPassThruCommand->DimmID = pDimm->DimmID; pPassThruCommand->Opcode = PtGetSecInfo; pPassThruCommand->SubOpcode= SubopGetSecState; pPassThruCommand->OutputPayloadSize = sizeof(*pSecurityPayload); ReturnCode = PassThru(pDimm, pPassThruCommand, Timeout); NVDIMM_DBG("PtReturnCode=" FORMAT_EFI_STATUS ", FwReturnCode=%d", ReturnCode, pPassThruCommand->Status); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on PassThru."); if FW_ERROR(pPassThruCommand->Status) { FW_CMD_ERROR_TO_EFI_STATUS(pPassThruCommand, ReturnCode); } goto FinishFreeMem; } pSecurityPayload = (PT_GET_SECURITY_PAYLOAD*) &pPassThruCommand->OutPayload; *pSecurityState = pSecurityPayload->SecurityStatus.AsUint32; ReturnCode = EFI_SUCCESS; FinishFreeMem: FREE_POOL_SAFE(pPassThruCommand); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Set DIMM security state @param [in] pDimm Pointer to DIMM @param [in] Opcode PassThru command opcode @param [in] Subopcode PassThru command subopcode @param [in] PayloadBufferSize Size of PassThru command payload @param [in] PayloadBuffer Input buffer location @param [in] Timeout The timeout in 100ns units, to use for the PassThru protocol @retval EFI_INVALID_PARAMETER Input parameters are not correct @retval EFI_DEVICE_ERROR Failed on PassThru protocol @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI SetDimmSecurityState( IN DIMM *pDimm, IN UINT8 Opcode, IN UINT8 SubOpcode, IN UINT16 PayloadBufferSize, IN VOID *pPayloadBuffer OPTIONAL, IN UINT64 Timeout ) { NVM_FW_CMD *pPassThruCommand = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if((pPayloadBuffer != NULL) && (PayloadBufferSize > IN_PAYLOAD_SIZE)) { NVDIMM_DBG("Buffer size exceeds input payload size."); goto Finish; } //Only SetSecurity Opcode supported if(Opcode != PtSetSecInfo) { goto Finish; } pPassThruCommand = AllocateZeroPool(sizeof(*pPassThruCommand)); if (pPassThruCommand == NULL) { NVDIMM_ERR("Out of memory."); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pPassThruCommand->DimmID = pDimm->DimmID; pPassThruCommand->Opcode = Opcode; pPassThruCommand->SubOpcode= SubOpcode; if (pPayloadBuffer != NULL) { CopyMem_S(&pPassThruCommand->InputPayload, sizeof(pPassThruCommand->InputPayload), pPayloadBuffer, PayloadBufferSize); pPassThruCommand->InputPayloadSize = PayloadBufferSize; } ReturnCode = PassThru(pDimm, pPassThruCommand, Timeout); NVDIMM_DBG("PtReturnCode=" FORMAT_EFI_STATUS ", FwReturnCode=%d", ReturnCode, pPassThruCommand->Status); if(EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on PassThru"); if (FW_ERROR(pPassThruCommand->Status)) { FW_CMD_ERROR_TO_EFI_STATUS(pPassThruCommand, ReturnCode); } goto FinishFreeMem; } ReturnCode = EFI_SUCCESS; FinishFreeMem: FREE_POOL_SAFE(pPassThruCommand); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Convert security bitmask to a defined state @param[in] SecurityFlag - mask from DIMM structure @param[out] pSecurityState - pointer to output with defined Security State **/ VOID ConvertSecurityBitmask( IN UINT32 SecurityFlag, OUT UINT8 *pSecurityState ) { NVDIMM_ENTRY(); if (pSecurityState == NULL) { return; } if (SecurityFlag & SECURITY_MASK_NOT_SUPPORTED) { *pSecurityState = SECURITY_NOT_SUPPORTED; } else if (SecurityFlag & SECURITY_MASK_FROZEN){ *pSecurityState = SECURITY_FROZEN; } else if (SecurityFlag & SECURITY_MASK_ENABLED) { if (SecurityFlag & SECURITY_MASK_COUNTEXPIRED) { *pSecurityState = SECURITY_PW_MAX; } else if (SecurityFlag & SECURITY_MASK_MASTER_COUNTEXPIRED) { *pSecurityState = SECURITY_MASTER_PW_MAX; } else if (SecurityFlag & SECURITY_MASK_LOCKED) { *pSecurityState = SECURITY_LOCKED; } else { *pSecurityState = SECURITY_UNLOCKED; } } else { *pSecurityState = SECURITY_DISABLED; } NVDIMM_EXIT(); } /** Convert security state to information if configuring is allowed @param[in] SecurityFlag Security mask from FW @retval TRUE if configuring is allowed @retval FALSE if configuring is not allowed **/ BOOLEAN IsConfiguringAllowed( IN UINT32 SecurityFlag ) { BOOLEAN IsAllowed = FALSE; IsAllowed = ( !(SecurityFlag & SECURITY_MASK_ENABLED) || !(SecurityFlag & SECURITY_MASK_LOCKED) || (SecurityFlag & SECURITY_MASK_NOT_SUPPORTED) ); return IsAllowed; } /** Convert security state to information if configuring for create goal is allowed @param[in] SecurityFlag Security mask from FW @retval TRUE if configuring for create goal is allowed @retval FALSE if configuring for create goal is not allowed **/ BOOLEAN IsConfiguringForCreateGoalAllowed( IN UINT32 SecurityFlag ) { BOOLEAN IsAllowed = FALSE; IsAllowed = !(SecurityFlag & SECURITY_MASK_ENABLED) || ((SecurityFlag & SECURITY_MASK_ENABLED) && !(SecurityFlag & SECURITY_MASK_LOCKED)); return IsAllowed; } ipmctl-03.00.00.0485/DcpmPkg/driver/Core/NvmSecurity.h000066400000000000000000000046001440615110200217640ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _NVM_SECURITY_H_ #define _NVM_SECURITY_H_ #include #include /** Get DIMM security state @param [in] DimmPid Pointer to DIMM @param [in] Timeout The timeout in 100ns units, to use for the PassThru protocol @param [out] pSecurityState DIMM security state @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_DEVICE_ERROR Failed on PassThru protocol @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI GetDimmSecurityState( IN DIMM *pDimm, IN UINT64 Timeout, OUT UINT32 *pSecurityState ); /** Set DIMM security state @param [in] pDimm Pointer to DIMM @param [in] Opcode PassThru command opcode @param [in] Subopcode PassThru command subopcode @param [in] PayloadBufferSize Size of PassThru command payload @param [in] PayloadBuffer Input buffer location @param [in] Timeout The timeout in 100ns units, to use for the PassThru protocol @retval EFI_INVALID_PARAMETER Input parameters are not correct @retval EFI_DEVICE_ERROR Failed on PassThru protocol @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI SetDimmSecurityState( IN DIMM *pDimm, IN UINT8 Opcode, IN UINT8 SubOpcode, IN UINT16 PayloadBufferSize, IN VOID *pPayloadBuffer OPTIONAL, IN UINT64 Timeout ); /** Convert security bitmask to a defined state @param[in] SecurityFlag - mask from DIMM structure @param[out] pSecurityState - pointer to output with defined Security State **/ VOID ConvertSecurityBitmask( IN UINT32 SecurityFlag, OUT UINT8 *pSecurityState ); /** Convert security state to information if configuring is allowed @param[in] SecurityFlag Security mask from FW @retval TRUE if configuring is allowed @retval FALSE if configuring is not allowed **/ BOOLEAN IsConfiguringAllowed( IN UINT32 SecurityFlag ); /** Convert security state to information if configuring for create goal is allowed @param[in] SecurityFlag Security mask from FW @retval TRUE if configuring for create goal is allowed @retval FALSE if configuring for create goal is not allowed **/ BOOLEAN IsConfiguringForCreateGoalAllowed( IN UINT32 SecurityFlag ); #endif /** _NVM_SECURITY_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Pfn.c000066400000000000000000000120351440615110200202130ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "Pfn.h" #include "PfnLayout.h" #include "Namespace.h" GUID gPfnAbstractionGuid = EFI_PFN_ABSTRACTION_GUID; /** Prepare a pfn namespace for use @param [in] RawSize Size of pfn namespace being created @param [in] LbaSize Size of a block in a created namespace @param [in] ParentUuid[] UUID label of the namespace @param [in out] pNamespace pointer to pfn namespace @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Null input @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS PfnInit( IN UINT64 RawSize, IN UINT32 LbaSize, IN GUID *pParentUuid, IN OUT VOID *pNamespace ) { PFN_INFO *pPfnInfo = NULL; PFN *pPfn = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; if (pParentUuid == NULL || pNamespace == NULL) { goto FinishWithError; } pPfn = AllocateZeroPool(sizeof(PFN)); if (pPfn == NULL) { NVDIMM_DBG("Memory allocation for %x bytes failed", sizeof(PFN)); ReturnCode = EFI_OUT_OF_RESOURCES; goto FinishWithError; } pPfnInfo = AllocateZeroPool(sizeof(PFN_INFO)); if (pPfnInfo == NULL) { NVDIMM_DBG("Memory allocation for %x bytes failed", sizeof(PFN_INFO)); ReturnCode = EFI_OUT_OF_RESOURCES; goto FinishWithError; } CopyMem_S(&pPfn->ParentUuid, sizeof(pPfn->ParentUuid), pParentUuid, sizeof(GUID)); pPfn->RawSize = RawSize; pPfn->LbaSize = LbaSize; pPfn->pNamespace = pNamespace; ReturnCode = ReadNamespaceBytes(pNamespace, PFN_INFO_BLOCK_OFFSET, pPfnInfo, sizeof(PFN_INFO)); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to read namespace bytes"); goto FinishWithError; } else { pPfn->DataOff = pPfnInfo->DataOff; pPfn->StartPad = pPfnInfo->StartPad; ((NAMESPACE *) pNamespace)->pPfn = pPfn; // Set blockcount to usable size, excluding metadata ((NAMESPACE *) pNamespace)->UsableSize = ((NAMESPACE *) pNamespace)->BlockCount - pPfnInfo->StartPad - pPfnInfo->EndTrunc - pPfnInfo->DataOff; goto Finish; } FinishWithError: FREE_POOL_SAFE(pPfn); pPfn = NULL; Finish: NVDIMM_EXIT_I64(ReturnCode); FREE_POOL_SAFE(pPfnInfo); return ReturnCode; } /** Read a block from a pfn namespace @param [in] pPfn namespace handle @param [in] Lba Logical block address to be read @param [out] pBuffer Read result Buffer pointer @retval EFI_SUCCESS if the routine succeeds **/ EFI_STATUS PfnRead( IN PFN *pPfn, IN UINT64 LBA, OUT VOID *pBuffer ) { UINT64 Offset = 0; NAMESPACE *pNamespace = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; pNamespace = pPfn->pNamespace; Offset = pPfn->StartPad + pPfn->DataOff + (LBA * pNamespace->Media.BlockSize); ReturnCode = ReadNamespaceBytes(pPfn->pNamespace, Offset, pBuffer, pPfn->LbaSize); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Pfn read failed %d", ReturnCode); return ReturnCode; } return EFI_SUCCESS; } /** Write a block to a pfn namespace @param [in] pPfn namespace handle @param [in] Lba Logical block address to be written @param [out] pBuffer Buffer pointer to the block to be written @retval EFI_SUCCESS if the routine succeeds **/ EFI_STATUS PfnWrite( IN PFN *pPfn, IN UINT64 LBA, IN VOID *pBuffer ) { UINT64 Offset = 0; NAMESPACE *pNamespace = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; pNamespace = pPfn->pNamespace; Offset = pPfn->StartPad + pPfn->DataOff + (LBA * pNamespace->Media.BlockSize); ReturnCode = WriteNamespaceBytes(pPfn->pNamespace, Offset, pBuffer, pPfn->LbaSize); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Pfn write failed %d", ReturnCode); return ReturnCode; } return EFI_SUCCESS; } /** Validates pfn info block @retval EFI_SUCCESS if the routine succeeds @retval EFI_ABORTED invalid pfn info block @param [in] pInfo Info block to be validated @param [in] pPfn PFN to be compared with existing metadata **/ EFI_STATUS PfnValidateInfo( IN PFN_INFO *pInfo, IN PFN *pPfn OPTIONAL ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pInfo == NULL) { goto Finish; } if (CompareMem(pInfo->Sig, PfnSig, PFN_INFO_SIG_LEN) != 0) { NVDIMM_DBG("Invalid PFN signature "); ReturnCode = EFI_ABORTED; goto Finish; } if (pPfn != NULL) { if (CompareMem(&pInfo->ParentUuid, &pPfn->ParentUuid, sizeof(GUID)) != 0) { NVDIMM_DBG("parent UUID mismatch"); ReturnCode = EFI_ABORTED; goto Finish; } } /* to be valid, the fields must Checksum correctly */ if (!ChecksumOperations((VOID *) pInfo, sizeof(PFN_INFO), &pInfo->Checksum, FALSE)) { NVDIMM_DBG("Invalid checksum"); ReturnCode = EFI_ABORTED; goto Finish; } /* to be valid, Info block must have Major version of at least 1 */ if (pInfo->Major == 0) { NVDIMM_DBG("Invalid major version(0)"); ReturnCode = EFI_ABORTED; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Pfn.h000066400000000000000000000051601440615110200202210ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _PFN_H_ #define _PFN_H_ #include "PfnLayout.h" extern GUID gPfnAbstractionGuid; #define EFI_PFN_ABSTRACTION_GUID \ { 0x266400BA, 0xFB9F, 0x4677, {0xBC, 0xB0, 0x96, 0x8F, 0x11, 0xD0, 0xD2, 0x25} } typedef struct _PFN { /** UUID of the PFN **/ GUID Uuid; /** UUID of the containing namespace, used to validate PFN metadata. **/ GUID ParentUuid; UINT64 RawSize; //!< Size of containing namespace UINT32 LbaSize; //!< External LBA size UINT32 StartPad; //!< Padding to align the capacity to a Linux "section" boundary (128MB) UINT64 DataOff; //!< Data offset relative to namespace_base + start pad VOID *pNamespace; // The pointer to the containing namespace for the IO operations } PFN; /** Signature for PFN info block. Total size is 16 bytes, including the '\0' added to the string by the declaration (the last two bytes of the string are '\0'). **/ static const char PfnSig[] = "NVDIMM_PFN_INFO\0"; /** Prepare a pfn namespace for use @param [in] RawSize Size of pfn namespace being created @param [in] LbaSize Size of a block in a created namespace @param [in] ParentUuid[] UUID label of the namespace @param [in] pNamespace pointer to the PFNs parent namespace @param [out] pPfn pointer to pfn struct to initialize @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Null input @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS PfnInit( IN UINT64 RawSize, IN UINT32 LbaSize, IN GUID *pParentUuid, IN VOID *pNamespace ); /** Read a block from a pfn namespace @param [in] pPfn namespace handle @param [in] Lba Logical block address to be read @param [out] pBuffer Read result Buffer pointer @retval EFI_SUCCESS if the routine succeeds **/ EFI_STATUS PfnRead( IN PFN *pPfn, IN UINT64 LBA, OUT VOID *pBuffer ); /** Write a block to a pfn namespace @param [in] pPfn namespace handle @param [in] Lba Logical block address to be written @param [out] pBuffer Buffer pointer to the block to be written @retval EFI_SUCCESS if the routine succeeds **/ EFI_STATUS PfnWrite( IN PFN *pPfn, IN UINT64 LBA, IN VOID *pBuffer ); /** Validates pfn info block @retval EFI_SUCCESS if the routine succeeds @retval EFI_ABORTED invalid pfn info block @param [in] pInfo Info block to be validated @param [in] pPfn PFN to be compared with existing metadata **/ EFI_STATUS PfnValidateInfo( IN PFN_INFO *pInfo, IN PFN *pPFN ); #endif //_PFN_H_ ipmctl-03.00.00.0485/DcpmPkg/driver/Core/PfnLayout.h000066400000000000000000000027551440615110200214260ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _PFN_LAYOUT_H_ #define _PFN_LAYOUT_H_ #define PFN_INFO_SIG_LEN 16 //!< Signature length #define PFN_INFO_BLOCK_OFFSET SIZE_4KB //!< PFN Info Block offset typedef struct _PFN_INFO { UINT8 Sig[PFN_INFO_SIG_LEN]; //!< must be "NVDIMM_PFN_INFO\0" GUID Uuid; //!< PFN UUID GUID ParentUuid; //!< UUID of container UINT32 Flags; //!< see flag bits below UINT16 Major; //!< major version UINT16 Minor; //!< minor version UINT64 DataOff; //!< data offset relative to namespace_base + start pad UINT64 NPfns; //!< number of page frames hosted by this info block UINT32 Mode; //!< memmap array storage location, see mode bits below /* minor-version-1 additions for section alignment */ UINT32 StartPad; //!< padding to align the capacity to a Linux "section" boundary (128MB) UINT32 EndTrunc; //!< reserved capacity to align to a Linux "section" boundary (128MB) /* minor-version-2 record the base alignment of the mapping */ UINT32 Align; //!< base alignment of the mapping UINT8 Unused[4000]; //!< alignment to PFN_ALIGNMENT 4096 UINT64 Checksum; //!< Fletcher64 checksum of all the fields } PFN_INFO; #endif // _PFN_LAYOUT_H_ ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Region.c000066400000000000000000005313351440615110200207240ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "Dimm.h" #include "Region.h" #include "Namespace.h" #include #include #include #include #include #include extern NVMDIMMDRIVER_DATA *gNvmDimmData; STATIC INT32 CompareRegionOffsetInDimmRegion( IN VOID *pFirst, IN VOID *pSecond ) { DIMM_REGION *pDimmRegion = NULL; DIMM_REGION *pDimmRegion2 = NULL; if (pFirst == NULL || pSecond == NULL) { NVDIMM_DBG("NULL pointer found."); return 0; } pDimmRegion = DIMM_REGION_FROM_NODE(pFirst); pDimmRegion2 = DIMM_REGION_FROM_NODE(pSecond); if (pDimmRegion->SpaRegionOffset < pDimmRegion2->SpaRegionOffset) { return -1; } else if (pDimmRegion->SpaRegionOffset > pDimmRegion2->SpaRegionOffset) { return 1; } else { return 0; } } /** Allocate and initialize the Interleave Set by using NFIT table @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pNvDimmRegionMappingStructure The NVDIMM region that helps describe this region of memory @param[in] RegionId The next consecutive region id @param[out] ppIS Interleave Set parent for new dimm region @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS InitializeISFromNfit( IN ParsedFitHeader *pFitHead, IN NvDimmRegionMappingStructure *pNvDimmRegionTbl, IN UINT16 RegionId, OUT NVM_IS **ppIS ) { EFI_STATUS ReturnCode = EFI_SUCCESS; InterleaveStruct *pInterleaveTbl = NULL; NVDIMM_ENTRY(); if (pFitHead == NULL || pNvDimmRegionTbl == NULL || ppIS == NULL) { return EFI_INVALID_PARAMETER; } *ppIS = AllocateZeroPool(sizeof(NVM_IS)); if (*ppIS == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } if (pNvDimmRegionTbl->InterleaveStructureIndex != 0) { ReturnCode = GetInterleaveTable(pFitHead, pNvDimmRegionTbl->InterleaveStructureIndex, &pInterleaveTbl); if (pInterleaveTbl == NULL) { NVDIMM_DBG("InterleaveStructure table with index: %d not found.", pNvDimmRegionTbl->InterleaveStructureIndex); goto Finish; } } InitializeListHead(&((*ppIS)->DimmRegionList)); InitializeListHead(&((*ppIS)->AppDirectNamespaceList)); (*ppIS)->Signature = IS_SIGNATURE; (*ppIS)->Size = 0; (*ppIS)->State = IS_STATE_HEALTHY; (*ppIS)->InterleaveSetIndex = pNvDimmRegionTbl->SpaRangeDescriptionTableIndex; (*ppIS)->RegionId = RegionId; (*ppIS)->SocketId = pNvDimmRegionTbl->DeviceHandle.NfitDeviceHandle.SocketId & MAX_UINT16; if (pInterleaveTbl != NULL) { (*ppIS)->InterleaveFormatChannel = pInterleaveTbl->LineSize & MAX_UINT16; (*ppIS)->InterleaveFormatImc = pInterleaveTbl->LineSize & MAX_UINT16; } (*ppIS)->InterleaveFormatWays = pNvDimmRegionTbl->InterleaveWays; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Allocate and initialize the Interleave Set by using Interleave Information table from Platform Config Data @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS InitializeIS( IN VOID *pInterleaveInfoTable, IN UINT16 RegionId, IN ACPI_REVISION PcdConfRevision, OUT NVM_IS **ppIS ) { EFI_STATUS Rc = EFI_SUCCESS; if (pInterleaveInfoTable == NULL || ppIS == NULL) { return EFI_INVALID_PARAMETER; } *ppIS = AllocateZeroPool(sizeof(NVM_IS)); if (*ppIS == NULL) { return EFI_OUT_OF_RESOURCES; } InitializeListHead(&((*ppIS)->DimmRegionList)); InitializeListHead(&((*ppIS)->AppDirectNamespaceList)); if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcdConfRevision)) { NVDIMM_INTERLEAVE_INFORMATION *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION *)pInterleaveInfoTable; (*ppIS)->Signature = IS_SIGNATURE; (*ppIS)->Size = 0; (*ppIS)->State = IS_STATE_HEALTHY; (*ppIS)->InterleaveSetIndex = pInterleaveInfo->InterleaveSetIndex; (*ppIS)->RegionId = RegionId; (*ppIS)->InterleaveFormatChannel = pInterleaveInfo->InterleaveFormatChannel; (*ppIS)->InterleaveFormatImc = pInterleaveInfo->InterleaveFormatImc; (*ppIS)->InterleaveFormatWays = pInterleaveInfo->InterleaveFormatWays; } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdConfRevision)) { NVDIMM_INTERLEAVE_INFORMATION3 *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION3 *)pInterleaveInfoTable; (*ppIS)->Signature = IS_SIGNATURE; (*ppIS)->Size = 0; (*ppIS)->State = IS_STATE_HEALTHY; (*ppIS)->InterleaveSetIndex = pInterleaveInfo->InterleaveSetIndex; (*ppIS)->RegionId = RegionId; (*ppIS)->InterleaveFormatChannel = pInterleaveInfo->InterleaveFormatChannel; (*ppIS)->InterleaveFormatImc = pInterleaveInfo->InterleaveFormatImc; GetBitFieldForNumOfChannelWays(pInterleaveInfo->NumOfDimmsInInterleaveSet, &(*ppIS)->InterleaveFormatWays); } return Rc; } /** Create and initialize all Interleave Sets When something goes wrong with particular Interleave Set then no additional Interleave Set structs created or error state on Interleave Set is set. @param[in] pFitHead NVM Firmware Interface Table @param[in] pDimmList Head of the list of all NVM DIMMs in the system @param[out] pISList Head of the list for Interleave Sets @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS InitializeISs( IN ParsedFitHeader *pFitHead, IN LIST_ENTRY *pDimmList, IN BOOLEAN UseNfit, OUT LIST_ENTRY *pISList ) { EFI_STATUS ReturnCode = EFI_SUCCESS; if (pFitHead == NULL || pDimmList == NULL || pISList == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (!UseNfit) { ReturnCode = RetrieveISsFromPlatformConfigData(pFitHead, pDimmList, pISList); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Retrieving Interleave Sets from the Platform Config Data failed."); goto Finish; } } else { ReturnCode = RetrieveISsFromNfit(pFitHead, pDimmList, pISList); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Retrieving Interleave Sets from NFIT table failed."); goto Finish; } } Finish: return ReturnCode; } /** Initialize interleave sets It initializes the interleave sets using NFIT or PCD @param[in] UseNfit Flag to indicate usage of NFIT or else default to PCD @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS InitializeInterleaveSets( IN BOOLEAN UseNfit ) { EFI_STATUS ReturnCode = EFI_SUCCESS; if (UseNfit ? !gNvmDimmData->PMEMDev.RegionsNfitInitialized : !gNvmDimmData->PMEMDev.RegionsAndNsInitialized) { ReturnCode = InitializeISs(gNvmDimmData->PMEMDev.pFitHead, &gNvmDimmData->PMEMDev.Dimms, UseNfit, (UseNfit ? &gNvmDimmData->PMEMDev.ISsNfit : &gNvmDimmData->PMEMDev.ISs)); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to retrieve the REGION list, error = " FORMAT_EFI_STATUS ".", ReturnCode); } else { if (!UseNfit) { gNvmDimmData->PMEMDev.RegionsAndNsInitialized = TRUE; } else { gNvmDimmData->PMEMDev.RegionsNfitInitialized = TRUE; } } } return ReturnCode; } /** Get Region by ID Scan the Region list for a Region identified by ID @param[in] pRegionList Head of the list for Regions @param[in] RegionId Region identifier @retval NVM_IS struct pointer if matching Region has been found @retval NULL pointer if not found **/ NVM_IS * GetRegionById( IN LIST_ENTRY *pRegionList, IN UINT16 RegionId ) { NVM_IS *pRegion = NULL; NVM_IS *pTargetRegion = NULL; LIST_ENTRY *pNode = NULL; NVDIMM_ENTRY(); LIST_FOR_EACH(pNode, pRegionList) { pRegion = IS_FROM_NODE(pNode); if (pRegion->RegionId == RegionId) { pTargetRegion = pRegion; break; } } NVDIMM_EXIT(); return pTargetRegion; } /** Get Region List Returns the pointer to the region list. It is also initializing the region list if it is necessary. @param[in] pRegionList Head of the list for Regions @param[in] UseNfit Flag to indicate usage of NFIT @retval pointer to the region list **/ EFI_STATUS GetRegionList( IN LIST_ENTRY **ppRegionList, IN BOOLEAN UseNfit ) { EFI_STATUS ReturnCode = EFI_SUCCESS; if (UseNfit ? !gNvmDimmData->PMEMDev.RegionsNfitInitialized : !gNvmDimmData->PMEMDev.RegionsAndNsInitialized) { ReturnCode = InitializeISs(gNvmDimmData->PMEMDev.pFitHead, &gNvmDimmData->PMEMDev.Dimms, UseNfit, (UseNfit ? &gNvmDimmData->PMEMDev.ISsNfit : &gNvmDimmData->PMEMDev.ISs)); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to retrieve the REGION list, error = " FORMAT_EFI_STATUS ".", ReturnCode); } else { if (!UseNfit) { gNvmDimmData->PMEMDev.RegionsAndNsInitialized = TRUE; } else { gNvmDimmData->PMEMDev.RegionsNfitInitialized = TRUE; } } } if (NULL != ppRegionList) { if (!UseNfit) { *ppRegionList = &gNvmDimmData->PMEMDev.ISs; //IS is region } else { *ppRegionList = &gNvmDimmData->PMEMDev.ISsNfit; } } return ReturnCode; } /** Clean the Interleave Set @param[in, out] pDimmList: the list of DCPMMs @param[in, out] pISList: the list of Interleave Sets to clean **/ VOID CleanISLists( IN OUT LIST_ENTRY *pDimmList, IN OUT LIST_ENTRY *pISList ) { NVM_IS *pIS = NULL; LIST_ENTRY *pISNode = NULL; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; NVDIMM_ENTRY(); if (pDimmList == NULL || pISList == NULL) { goto Finish; } /** Free Interleave Sets and Dimm Regions. Remove them from the Interleave Set list. **/ while (!IsListEmpty(pISList)) { pISNode = GetFirstNode(pISList); pIS = IS_FROM_NODE(pISNode); RemoveEntryList(pISNode); FreeISResources(pIS); } /** Clean pointers in Dimms **/ LIST_FOR_EACH(pDimmNode, pDimmList) { pDimm = DIMM_FROM_NODE(pDimmNode); pDimm->ISsNum = 0; pDimm->IsRegionsNum = 0; pDimm->ISsNfitNum = 0; pDimm->IsRegionsNfitNum = 0; } Finish: NVDIMM_EXIT(); } /** Free a Interleave Set and all memory resources in use by the Interleave Set. @param[in, out] pIS the Interleave Set and its regions that will be released **/ VOID FreeISResources( IN OUT NVM_IS *pIS ) { DIMM_REGION *pDimmRegion = NULL; LIST_ENTRY *pDimmRegionNode = NULL; LIST_ENTRY *pDimmRegionNextNode = NULL; NVDIMM_ENTRY(); if (pIS == NULL) { goto Finish; } /** Free regions which the Interleave Set is composed from **/ LIST_FOR_EACH_SAFE(pDimmRegionNode, pDimmRegionNextNode, &pIS->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pDimmRegionNode); RemoveEntryList(pDimmRegionNode); FreePool(pDimmRegion); } FreePool(pIS); Finish: NVDIMM_EXIT(); } /** Allocate and initialize the dimm region by using NFIT table @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pDimm Target DIMM structure pointer @param[in] pISList List of interleave sets formed so far @param[in] pNvDimmRegionMappingStructure The NVDIMM region that helps describe this region of memory @param[out] pRegionId The next consecutive region id @param[out] ppNewIS Interleave Set parent for new dimm region @param[out] ppDimmRegion new allocated dimm region will be put here @param[out] pISDimmRegionAlreadyExists TRUE if Interleave Set DIMM region already exists @retval EFI_SUCCESS @retval EFI_NOT_FOUND the Dimm related with DimmRegion has not been found on the Dimm list @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS InitializeDimmRegionFromNfit( IN ParsedFitHeader *pFitHead, IN DIMM *pDimm, IN LIST_ENTRY *pISList, IN NvDimmRegionMappingStructure *pNvDimmRegionMappingStructure, OUT UINT16 *pRegionId, OUT NVM_IS **ppCurrentIS, OUT DIMM_REGION **ppDimmRegion, OUT BOOLEAN *pISDimmRegionAlreadyExists ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LIST_ENTRY *pISNode = NULL; NVM_IS *pExistingIS = NULL; DIMM_REGION *pDimmRegion = NULL; LIST_ENTRY *pDimmRegionNode = NULL; NVM_IS *pNewIS = NULL; BOOLEAN ISAlreadyExists = FALSE; NVDIMM_ENTRY(); if (pDimm == NULL || pISList == NULL || pNvDimmRegionMappingStructure == NULL || pRegionId == NULL || ppDimmRegion == NULL || ppCurrentIS == NULL || pISDimmRegionAlreadyExists == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pISDimmRegionAlreadyExists = FALSE; /** Check if Interleave Set already exists for this Interleave Set Index **/ LIST_FOR_EACH(pISNode, pISList) { pExistingIS = IS_FROM_NODE(pISNode); if (pExistingIS->InterleaveSetIndex == pNvDimmRegionMappingStructure->SpaRangeDescriptionTableIndex) { ISAlreadyExists = TRUE; *ppCurrentIS = pExistingIS; LIST_FOR_EACH(pDimmRegionNode, &pExistingIS->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pDimmRegionNode); if (pDimm->SerialNumber == pDimmRegion->pDimm->SerialNumber) { *pISDimmRegionAlreadyExists = TRUE; goto Finish; } } } } if (!(*pISDimmRegionAlreadyExists)) { if (!ISAlreadyExists) { ReturnCode = InitializeISFromNfit(pFitHead, pNvDimmRegionMappingStructure, *pRegionId, &pNewIS); if (EFI_ERROR(ReturnCode) || pNewIS == NULL) { FREE_POOL_SAFE(pNewIS); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } (*pRegionId)++; InsertTailList(pISList, &(pNewIS->IsNode)); *ppCurrentIS = pNewIS; } *ppDimmRegion = AllocateZeroPool(sizeof(DIMM_REGION)); if (*ppDimmRegion == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } (*ppDimmRegion)->pDimm = pDimm; if (MAX_IS_PER_DIMM <= pDimm->ISsNfitNum) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pDimm->pISsNfit[pDimm->ISsNfitNum] = *ppCurrentIS; pDimm->ISsNfitNum++; (*ppDimmRegion)->Signature = DIMM_REGION_SIGNATURE; (*ppDimmRegion)->PartitionOffset = pNvDimmRegionMappingStructure->NvDimmPhysicalAddressRegionBase - pDimm->PmStart; (*ppDimmRegion)->PartitionSize = pNvDimmRegionMappingStructure->NvDimmRegionSize; } else { *ppCurrentIS = NULL; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Allocate and initialize the dimm region by using Interleave Information table from Platform Config Data @param[in] pCurDimm the DIMM from which Interleave Information table was retrieved @param[in] pDimmList Head of the list of all Intel NVM Dimm in the system @param[in] pISList List of interleave sets formed so far @param[in] pIdentificationInfoTable Identification Information table @param[in] pInterleaveInfoTable Interleave information for the particular dimm @param[in] PcdConfRevision Revision of the PCD Config tables @param[out] pRegionId The next consecutive region id @param[out] ppNewIS Interleave Set parent for new dimm region @param[out] ppDimmRegion new allocated dimm region will be put here @param[out] pISAlreadyExists TRUE if Interleave Set already exists @retval EFI_SUCCESS @retval EFI_NOT_FOUND the Dimm related with DimmRegion has not been found on the Dimm list @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS InitializeDimmRegion( IN DIMM *pCurDimm, IN LIST_ENTRY *pDimmList, IN LIST_ENTRY *pISList, IN VOID *pIdentificationInfoTable, IN VOID *pInterleaveInfoTable, IN ACPI_REVISION PcdConfRevision, OUT UINT16 *pRegionId, OUT NVM_IS **ppNewIS, OUT DIMM_REGION **ppDimmRegion, OUT BOOLEAN *pISAlreadyExists ) { EFI_STATUS Rc = EFI_SUCCESS; DIMM *pDimm = NULL; UINT16 ManufacturerInPcd = 0; UINT32 SerialNumberInPcd = 0; LIST_ENTRY *pISNode = NULL; NVM_IS *pExistingIS = NULL; DIMM_REGION *pDimmRegion = NULL; LIST_ENTRY *pDimmRegionNode = NULL; DIMM_UNIQUE_IDENTIFIER DimmUidInPcd; UINT16 InterleaveSetIndex = 0; UINT64 PartitionOffset = 0; UINT64 PartitionSize = 0; NVDIMM_ENTRY(); ZeroMem(&DimmUidInPcd, sizeof(DimmUidInPcd)); if (pCurDimm == NULL || pDimmList == NULL || pISList == NULL || pIdentificationInfoTable == NULL || pInterleaveInfoTable == NULL || pRegionId == NULL || ppDimmRegion == NULL || ppNewIS == NULL || pISAlreadyExists == NULL) { Rc = EFI_INVALID_PARAMETER; goto Finish; } *pISAlreadyExists = FALSE; if (IS_ACPI_REV_INVALID(PcdConfRevision)) { Rc = EFI_INVALID_PARAMETER; NVDIMM_DBG("Error: Invalid revision value %d for PCD config table.", PcdConfRevision); goto Finish; } if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcdConfRevision)) { NVDIMM_INTERLEAVE_INFORMATION *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION *)pInterleaveInfoTable; NVDIMM_IDENTIFICATION_INFORMATION *pIdentificationInfo = (NVDIMM_IDENTIFICATION_INFORMATION *)pIdentificationInfoTable; if (IS_ACPI_REV_MAJ_0_MIN_1(PcdConfRevision)) { ManufacturerInPcd = pIdentificationInfo->DimmIdentification.Version1.DimmManufacturerId; SerialNumberInPcd = pIdentificationInfo->DimmIdentification.Version1.DimmSerialNumber; } else { CopyMem_S(&DimmUidInPcd, sizeof(DimmUidInPcd), &pIdentificationInfo->DimmIdentification.Version2.Uid, sizeof(DIMM_UNIQUE_IDENTIFIER)); ManufacturerInPcd = DimmUidInPcd.ManufacturerId; SerialNumberInPcd = DimmUidInPcd.SerialNumber; } InterleaveSetIndex = pInterleaveInfo->InterleaveSetIndex; PartitionOffset = pIdentificationInfo->PartitionOffset; PartitionSize = pIdentificationInfo->PmPartitionSize; } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdConfRevision)) { NVDIMM_INTERLEAVE_INFORMATION3 *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION3 *)pInterleaveInfoTable; NVDIMM_IDENTIFICATION_INFORMATION3 *pIdentificationInfo = (NVDIMM_IDENTIFICATION_INFORMATION3 *)pIdentificationInfoTable; CopyMem_S(&DimmUidInPcd, sizeof(DimmUidInPcd), &pIdentificationInfo->DimmIdentification, sizeof(DIMM_UNIQUE_IDENTIFIER)); ManufacturerInPcd = DimmUidInPcd.ManufacturerId; SerialNumberInPcd = DimmUidInPcd.SerialNumber; InterleaveSetIndex = pInterleaveInfo->InterleaveSetIndex; PartitionOffset = pIdentificationInfo->PartitionOffset; PartitionSize = pIdentificationInfo->PmPartitionSize; } if (ManufacturerInPcd == 0 || SerialNumberInPcd == 0) { NVDIMM_DBG("Serial or manufacturer number in the Identification Information table is equal to 0."); Rc = EFI_DEVICE_ERROR; goto Finish; } if (IS_ACPI_REV_MAJ_0_MIN_1(PcdConfRevision)) { pDimm = GetDimmBySerialNumber(pDimmList, SerialNumberInPcd); } else { pDimm = GetDimmByUniqueIdentifier(pDimmList, DimmUidInPcd); } if (pDimm == NULL) { Rc = EFI_NOT_FOUND; NVDIMM_DBG("Dimm not found using the Identification Information table"); goto Finish; } /** Check if Interleave Set already exists for this Interleave Set Index **/ LIST_FOR_EACH(pISNode, pISList) { pExistingIS = IS_FROM_NODE(pISNode); if (pExistingIS->InterleaveSetIndex == InterleaveSetIndex) { LIST_FOR_EACH(pDimmRegionNode, &pExistingIS->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pDimmRegionNode); /** Addressing the corner case where a dimm is moved from another system and has the same interleave set index **/ if (pDimm->SerialNumber == pDimmRegion->pDimm->SerialNumber) { *pISAlreadyExists = TRUE; goto Finish; } } } } if (!(*pISAlreadyExists)) { /* As this method is called inside the for loop for each dimm Interleave Information table, it could be that ppNewIS is initialized the first time. Ignore it if it is already initialized and added to the list. Avoiding duplicates.*/ if (*ppNewIS == NULL) { Rc = InitializeIS(pInterleaveInfoTable, *pRegionId, PcdConfRevision, ppNewIS); if (EFI_ERROR(Rc) || *ppNewIS == NULL) { Rc = EFI_OUT_OF_RESOURCES; goto Finish; } (*pRegionId)++; InsertTailList(pISList, &((*ppNewIS)->IsNode)); } *ppDimmRegion = AllocateZeroPool(sizeof(DIMM_REGION)); if (*ppDimmRegion == NULL) { Rc = EFI_OUT_OF_RESOURCES; goto Finish; } (*ppDimmRegion)->pDimm = pDimm; /** Insert only mapped/healthy regions into pDimm->pISs array. PCD is not updated by BIOS on non-functional DIMMS. So non-functional DIMMs need to be excluded to avoid false indication of being in configured state. **/ if (pCurDimm->Configured && !pCurDimm->NonFunctional) { if (MAX_IS_PER_DIMM <= pDimm->ISsNum) { Rc = EFI_OUT_OF_RESOURCES; goto Finish; } pDimm->pISs[pDimm->ISsNum] = *ppNewIS; pDimm->ISsNum++; } (*ppDimmRegion)->Signature = DIMM_REGION_SIGNATURE; (*ppDimmRegion)->PartitionOffset = PartitionOffset; (*ppDimmRegion)->PartitionSize = PartitionSize; } Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** Retrieve Interleave Sets by using NFIT table Using the parsed NFIT table data to get information about Interleave Sets configuration. @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pDimmList Head of the list of all NVM DIMMs in the system @param[out] pISList Head of the list for Interleave Sets @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RetrieveISsFromNfit( IN ParsedFitHeader *pFitHead, IN LIST_ENTRY *pDimmList, OUT LIST_ENTRY *pISList ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; DIMM_REGION *pNewDimmRegion = NULL; DIMM_REGION *pDimmRegion = NULL; NVM_IS *pIS = NULL; NvDimmRegionMappingStructure *pNvDimmRegionMappingStructure = NULL; LIST_ENTRY *pISNode = NULL; BOOLEAN UseLatestVersion = FALSE; BOOLEAN ISDimmRegionAlreadyExists = FALSE; UINT32 Index = 0; UINT16 RegionId = 1; // region id used internally to distinguish different regions. UINT32 IsRegionIndex = 0; UINT32 NumOfDimmsInInterleaveSet = 0; NVDIMM_ENTRY(); for (Index = 0; Index < pFitHead->NvDimmRegionMappingStructuresNum; Index++) { // Look for NVDIMM regions which have a SPA mapping for PM region type ReturnCode = GetNvDimmRegionMappingStructureForPid(pFitHead, pFitHead->ppNvDimmRegionMappingStructures[Index]->NvDimmPhysicalId, &gSpaRangePmRegionGuid, TRUE, pFitHead->ppNvDimmRegionMappingStructures[Index]->SpaRangeDescriptionTableIndex, &pNvDimmRegionMappingStructure); if (ReturnCode == EFI_NOT_FOUND) { NVDIMM_WARN("No NVDIMM region table found with SPA Range PM Region GUID"); ReturnCode = EFI_SUCCESS; continue; } else if (EFI_ERROR(ReturnCode)) { goto Finish; } pDimm = GetDimmByPid(pFitHead->ppNvDimmRegionMappingStructures[Index]->NvDimmPhysicalId, pDimmList); if (!IsDimmManageable(pDimm)) { continue; } ReturnCode = InitializeDimmRegionFromNfit(pFitHead, pDimm, pISList, pNvDimmRegionMappingStructure, &RegionId, &pIS, &pNewDimmRegion, &ISDimmRegionAlreadyExists); if (pIS == NULL) { FREE_POOL_SAFE(pNewDimmRegion); goto Finish; } if (ISDimmRegionAlreadyExists) { continue; } if (EFI_ERROR(ReturnCode) || pNewDimmRegion == NULL) { pIS->State = SetISStateWithPriority(pIS->State, IS_STATE_INIT_FAILURE); NVDIMM_DBG("One of parameters was NULL or out of memory"); } else { InsertTailList(&pIS->DimmRegionList, &pNewDimmRegion->DimmRegionNode); IsRegionIndex = pNewDimmRegion->pDimm->IsRegionsNfitNum; if (MAX_IS_PER_DIMM <= IsRegionIndex) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pNewDimmRegion->pDimm->pIsRegionsNfit[IsRegionIndex] = pDimmRegion; pNewDimmRegion->pDimm->IsRegionsNfitNum = IsRegionIndex + 1; pIS->Size += pNewDimmRegion->PartitionSize; } } LIST_FOR_EACH(pISNode, pISList) { pIS = IS_FROM_NODE(pISNode); if (pIS == NULL) { continue; } // Check if any interleave set is broken ReturnCode = GetListSize(&pIS->DimmRegionList, &NumOfDimmsInInterleaveSet); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (NumOfDimmsInInterleaveSet < pIS->InterleaveFormatWays) { pIS->State = SetISStateWithPriority(pIS->State, IS_STATE_DIMM_MISSING); NVDIMM_DBG("The Dimm related with the DimmRegion has not been found on the Dimm list"); } // AppDirect Mapping already exists if (pIS->pSpaTbl != NULL) { continue; } ReturnCode = RetrieveAppDirectMappingFromNfit(pFitHead, pIS); if (EFI_ERROR(ReturnCode)) { pIS->State = SetISStateWithPriority(pIS->State, IS_STATE_SPA_MISSING); NVDIMM_DBG("Couldn't retrieve AppDirect I/O structures from NFIT."); } ReturnCode = UseLatestNsLabelVersion(pIS, NULL, &UseLatestVersion); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (UseLatestVersion) { ReturnCode = CalculateISetCookie(pFitHead, pIS); if (EFI_ERROR(ReturnCode)) { goto Finish; } } else { ReturnCode = CalculateISetCookieVer1_1(pFitHead, pIS); if (EFI_ERROR(ReturnCode)) { goto Finish; } } ReturnCode = BubbleSortLinkedList(&pIS->DimmRegionList, CompareRegionOffsetInDimmRegion); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to sort DIMM regions in interleave set: 0x%x", pIS->InterleaveSetIndex); } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve Interleave Sets by using Platform Config Data from Intel manageable NVM Dimms Using the Platform Config Data command to get information about Interleave Sets configuration. @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pDimmList Head of the list of all NVM DIMMs in the system @param[out] pISList Head of the list for Interleave Sets @param[out] pRegionList Head of the list for Regions @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RetrieveISsFromPlatformConfigData( IN ParsedFitHeader *pFitHead, IN LIST_ENTRY *pDimmList, OUT LIST_ENTRY *pISList ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS IReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; NVDIMM_CONFIGURATION_HEADER *pPcdConfHeader = NULL; NVDIMM_CURRENT_CONFIG *pPcdCurrentConf = NULL; CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *pConfigManagementAttributesInfo = NULL; PCAT_TABLE_HEADER *pCurPcatTable = NULL; UINT32 SizeOfPcatTables = 0; UINT16 RegionId = 1; // region id used internally to distinguish different regions. Will be used when creating namespace. if (pDimmList == NULL || pISList == NULL) { return EFI_INVALID_PARAMETER; } LIST_FOR_EACH(pDimmNode, pDimmList) { pDimm = DIMM_FROM_NODE(pDimmNode); // Set default values pDimm->ConfigStatus = DIMM_CONFIG_UNDEFINED; pDimm->IsNew = 0; pDimm->Configured = FALSE; if (!IsDimmManageable(pDimm) || DIMM_MEDIA_NOT_ACCESSIBLE(pDimm->BootStatusBitmask)) { continue; } // Free previous use of pcd header if needed FREE_POOL_SAFE(pPcdConfHeader); ReturnCode = GetPlatformConfigDataOemPartition(pDimm, FALSE, &pPcdConfHeader); #ifdef MEMORY_CORRUPTION_WA if (ReturnCode == EFI_DEVICE_ERROR) { ReturnCode = GetPlatformConfigDataOemPartition(pDimm, FALSE, &pPcdConfHeader); } #endif // MEMORY_CORRUPTIO_WA if (EFI_ERROR(ReturnCode)) { // Ignore all errors except for PMem module busy with sanitize operation if (EFI_NO_RESPONSE == ReturnCode) { /* Save the return code here and continue with the execution for rest of the dimms. This is done to make the UEFI initialization succeed. During UEFI init, return code will be ignored but we have to error out when the actual command is executed. */ IReturnCode = ReturnCode; } ReturnCode = EFI_SUCCESS; continue; } if (pPcdConfHeader->CurrentConfStartOffset == 0 || pPcdConfHeader->CurrentConfDataSize == 0) { NVDIMM_DBG("There is no Current Config table"); continue; } pPcdCurrentConf = GET_NVDIMM_CURRENT_CONFIG(pPcdConfHeader); if (!IsPcdCurrentConfHeaderValid(pPcdCurrentConf, pDimm->PcdOemPartitionSize)) { continue; } pDimm->ConfigStatus = (UINT8)pPcdCurrentConf->ConfigStatus; pDimm->IsNew = (pDimm->ConfigStatus == DIMM_CONFIG_NEW_DIMM) ? 1 : 0; switch (pPcdCurrentConf->ConfigStatus) { case DIMM_CONFIG_SUCCESS: case DIMM_CONFIG_OLD_CONFIG_USED: // 2LM is not mapped because of NM:FM violation, but 1LM is mapped/healthy case DIMM_CONFIG_DCPMM_NM_FM_RATIO_UNSUPPORTED: case DIMM_CONFIG_PM_MAPPED_VM_POPULATION_ISSUE: pDimm->Configured = TRUE; break; default: pDimm->Configured = FALSE; break; } pDimm->MappedVolatileCapacity = pPcdCurrentConf->VolatileMemSizeIntoSpa; pDimm->MappedPersistentCapacity = pPcdCurrentConf->PersistentMemSizeIntoSpa; pCurPcatTable = (PCAT_TABLE_HEADER *) &pPcdCurrentConf->pPcatTables; SizeOfPcatTables = pPcdConfHeader->CurrentConfDataSize - (UINT32)((UINT8 *)pCurPcatTable - (UINT8 *)pPcdCurrentConf); /** Example of the use of the while loop condition Extension table #1 offset: 0 size: 10 Extension table #2 offset: 10 size: 5 Size of extension tables: 15 (10 + 5) Iteration #1: offset: 0 Iteration #2: offset: 10 Iteration #3: offset: 15 stop the loop: offset isn't less than size **/ while ((UINT32) ((UINT8 *)pCurPcatTable - (UINT8 *) &pPcdCurrentConf->pPcatTables) < SizeOfPcatTables) { if (pCurPcatTable->Type == PCAT_TYPE_INTERLEAVE_INFORMATION_TABLE) { if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pPcdCurrentConf)) { NVDIMM_INTERLEAVE_INFORMATION *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION *) pCurPcatTable; RetrieveISFromInterleaveInformationTable(pFitHead, pDimmList, pInterleaveInfo, pPcdCurrentConf->Header.Revision, pDimm, &RegionId, pISList); pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pInterleaveInfo->Header.Length); } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPcdCurrentConf)) { NVDIMM_INTERLEAVE_INFORMATION3 *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION3 *)pCurPcatTable; RetrieveISFromInterleaveInformationTable(pFitHead, pDimmList, pInterleaveInfo, pPcdCurrentConf->Header.Revision, pDimm, &RegionId, pISList); pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pInterleaveInfo->Header.Length); } } else if (pCurPcatTable->Type == PCAT_TYPE_CONFIG_MANAGEMENT_ATTRIBUTES_TABLE) { pConfigManagementAttributesInfo = (CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *) pCurPcatTable; pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, pConfigManagementAttributesInfo->Header.Length); } else { NVDIMM_DBG("This type (%d) of PCAT table shouldn't be contained in Current Configuration table", pCurPcatTable->Type); ReturnCode = EFI_DEVICE_ERROR; break; } } FREE_POOL_SAFE(pPcdConfHeader); pPcdConfHeader = NULL; } FREE_POOL_SAFE(pPcdConfHeader); return IReturnCode != EFI_SUCCESS ? IReturnCode : ReturnCode; } /** Parse Interleave Information table and create a Interleave Set if it doesn't exist yet. @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pDimmList Head of the list of all Intel NVM Dimms in the system @param[in] pInterleaveInfo Interleave Information table retrieve from DIMM @param[in] PcdCurrentConfRevision PCD Current Config table revision @param[in] pDimm the DIMM from which Interleave Information table was retrieved @param[in out] pRegionId Unique id for region @param[out] pISList Head of the list for Interleave Sets @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RetrieveISFromInterleaveInformationTable( IN ParsedFitHeader *pFitHead, IN LIST_ENTRY *pDimmList, IN VOID *pInterleaveInfoTable, IN ACPI_REVISION PcdCurrentConfRevision, IN DIMM *pDimm, IN OUT UINT16 *pRegionId, OUT LIST_ENTRY *pISList ) { EFI_STATUS Rc = EFI_SUCCESS; NVM_IS *pIS = NULL; DIMM_REGION *pDimmRegion = NULL; UINT32 Index = 0; UINT32 IsRegionIndex = 0; VOID *pCurrentIdentInfo = NULL; BOOLEAN UseLatestVersion = FALSE; BOOLEAN ISAlreadyExists = FALSE; UINT8 NumOfDimmsInInterleaveSet = 0; NVDIMM_ENTRY(); if (pFitHead == NULL || pDimmList == NULL || pInterleaveInfoTable == NULL || pDimm == NULL || pRegionId == NULL || pISList == NULL) { Rc = EFI_INVALID_PARAMETER; goto Finish; } /** Initialize Interleave Set and Dimm Regions **/ if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcdCurrentConfRevision)) { NVDIMM_INTERLEAVE_INFORMATION *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION *)pInterleaveInfoTable; pCurrentIdentInfo = (NVDIMM_IDENTIFICATION_INFORMATION *) &pInterleaveInfo->pIdentificationInfoList; NumOfDimmsInInterleaveSet = pInterleaveInfo->NumOfDimmsInInterleaveSet; } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdCurrentConfRevision)) { NVDIMM_INTERLEAVE_INFORMATION3 *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION3 *)pInterleaveInfoTable; pCurrentIdentInfo = (NVDIMM_IDENTIFICATION_INFORMATION3 *) &pInterleaveInfo->pIdentificationInfoList; NumOfDimmsInInterleaveSet = pInterleaveInfo->NumOfDimmsInInterleaveSet; } for (Index = 0; Index < NumOfDimmsInInterleaveSet; Index++) { Rc = InitializeDimmRegion(pDimm, pDimmList, pISList, pCurrentIdentInfo, pInterleaveInfoTable, PcdCurrentConfRevision, pRegionId, &pIS, &pDimmRegion, &ISAlreadyExists); // pIS will be null when the IS already exist or when there is no memory to do malloc. In either case go to Finish. if (ISAlreadyExists || pIS == NULL) { goto Finish; } if ((EFI_ERROR(Rc) && Rc != EFI_NOT_FOUND) || pDimmRegion == NULL) { pIS->State = SetISStateWithPriority(pIS->State, IS_STATE_INIT_FAILURE); NVDIMM_DBG("One of parameters was NULL or out of memory"); } else { if (Rc == EFI_NOT_FOUND) { pIS->State = SetISStateWithPriority(pIS->State, IS_STATE_DIMM_MISSING); NVDIMM_DBG("The Dimm related with the DimmRegion has not been found on the Dimm list"); } else { InsertTailList(&pIS->DimmRegionList, &pDimmRegion->DimmRegionNode); IsRegionIndex = pDimmRegion->pDimm->IsRegionsNum; /** Insert only mapped/healthy DimmRegions into pDimm->pIsRegions array. PCD is not updated by BIOS on non-functional DIMMS. So non-functional DIMMs need to be excluded to avoid false indication of being in configured state. **/ if (pDimm->Configured && !pDimm->NonFunctional) { if (MAX_IS_PER_DIMM <= IsRegionIndex) { Rc = EFI_OUT_OF_RESOURCES; goto Finish; } pDimmRegion->pDimm->pIsRegions[IsRegionIndex] = pDimmRegion; pDimmRegion->pDimm->IsRegionsNum = IsRegionIndex + 1; } pIS->Size += pDimmRegion->PartitionSize; } } if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcdCurrentConfRevision)) { pCurrentIdentInfo = (UINT8 *)pCurrentIdentInfo + sizeof(NVDIMM_IDENTIFICATION_INFORMATION); } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdCurrentConfRevision)) { pCurrentIdentInfo = (UINT8 *)pCurrentIdentInfo + sizeof(NVDIMM_IDENTIFICATION_INFORMATION3); } } if (pIS != NULL && !pDimm->Configured) { pIS->State = SetISStateWithPriority(pIS->State, IS_STATE_CONFIG_INACTIVE); } Rc = RetrieveAppDirectMappingFromNfit(pFitHead, pIS); if (pIS != NULL) { if (EFI_ERROR(Rc)) { pIS->State = SetISStateWithPriority(pIS->State, IS_STATE_SPA_MISSING); NVDIMM_DBG("Couldn't retrieve AppDirect I/O structures from NFIT."); } pIS->SocketId = pDimm->SocketId; Rc = UseLatestNsLabelVersion(pIS, NULL, &UseLatestVersion); if (EFI_ERROR(Rc)) { goto Finish; } if (UseLatestVersion) { Rc = CalculateISetCookie(pFitHead, pIS); if (EFI_ERROR(Rc)) { goto Finish; } } else { Rc = CalculateISetCookieVer1_1(pFitHead, pIS); if (EFI_ERROR(Rc)) { goto Finish; } } Rc = BubbleSortLinkedList(&pIS->DimmRegionList, CompareRegionOffsetInDimmRegion); if (EFI_ERROR(Rc)) { goto Finish; } } Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** Determine Region Type based on the Interleave sets @param[in, out] pRegion The region whose type needs to be determined @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ EFI_STATUS DetermineRegionType( IN NVM_IS *pRegion, OUT UINT8 *pRegionType ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 DimmCount = 0; NVDIMM_ENTRY(); if (pRegion == NULL || NULL == pRegionType) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pRegionType = 0; ReturnCode = GetListSize(&pRegion->DimmRegionList, &DimmCount); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Each interleave set will provide region type **/ if (DimmCount == 1) { *pRegionType |= PM_TYPE_AD_NI; } else if (DimmCount > 1) { *pRegionType |= PM_TYPE_AD; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Calculate free Region capacity @param[in] pRegion Region that a free capacity will be calculated for @param[out] pFreeCapacity Output parameter for result @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS GetFreeRegionCapacity( IN NVM_IS *pRegion, OUT UINT64 *pFreeCapacity ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LIST_ENTRY *pNode = NULL; UINT64 NamespaceCapacityUsed = 0; NAMESPACE *pNamespace = NULL; NVDIMM_ENTRY(); if (pRegion == NULL || pFreeCapacity == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } LIST_FOR_EACH(pNode, &pRegion->AppDirectNamespaceList) { pNamespace = NAMESPACE_FROM_NODE(pNode, IsNode); NamespaceCapacityUsed += GetRawCapacity(pNamespace); } *pFreeCapacity = pRegion->Size - NamespaceCapacityUsed; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Determine Regions health based on health state of Interleave Sets @param[in] pRegion The region whose health is to be determined @param[out] pHealthState The health state of the region @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Could not allocate memory **/ EFI_STATUS DetermineRegionHealth( IN NVM_IS *pRegion, OUT UINT16 *pHealthState ) { EFI_STATUS ReturnCode = EFI_SUCCESS; BOOLEAN IsLocked = FALSE; BOOLEAN HasNewGoal = FALSE; DIMM *pDimm = NULL; LIST_ENTRY *pNode = NULL; DIMM_REGION *pDimmRegion = NULL; NVDIMM_ENTRY(); if ((pRegion == NULL) || (pHealthState == NULL)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pHealthState = RegionHealthStateNormal; ReturnCode = RetrieveGoalConfigsFromPlatformConfigData(&gNvmDimmData->PMEMDev.Dimms, FALSE); if (EFI_ERROR(ReturnCode)) { goto FinishAdvance; } LIST_FOR_EACH(pNode, &pRegion->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pNode); pDimm = pDimmRegion->pDimm; if (!IsDimmManageable(pDimm) || DIMM_MEDIA_NOT_ACCESSIBLE(pDimm->BootStatusBitmask)) { continue; } /** Check if any of the DIMMs are locked **/ ReturnCode = IsDimmLocked(pDimm, &IsLocked); if (EFI_ERROR(ReturnCode)) { goto FinishAdvance; } if (IsLocked) { *pHealthState = RegionHealthStateLocked; break; } /** Check if any of the DIMMs have a config goal created, but not yet applied **/ ReturnCode = FindIfNewGoalOnDimm(pDimm, &HasNewGoal); if (EFI_ERROR(ReturnCode)) { goto FinishAdvance; } if (HasNewGoal) { *pHealthState = RegionHealthStatePending; break; } } IsLocked = FALSE; HasNewGoal = FALSE; /** Check for the static health states **/ if (*pHealthState == RegionHealthStateNormal) { if (pRegion->State != IS_STATE_HEALTHY) { switch (pRegion->State) { case IS_STATE_INIT_FAILURE: case IS_STATE_DIMM_MISSING: case IS_STATE_CONFIG_INACTIVE: case IS_STATE_SPA_MISSING: *pHealthState = RegionHealthStateError; break; default: *pHealthState = RegionHealthStateUnknown; break; } } } FinishAdvance: ClearInternalGoalConfigsInfo(&gNvmDimmData->PMEMDev.Dimms); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Determine if a set of dimms is configuring a given socket @param[in] DimmsNum Number of DIMMs to verify on socket @param[in] SocketId SocketId to verify that dimms are configuring @param[out] pWholeSocket True if dimms are fully configuring a socket @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER if one or more parameters are NULL **/ STATIC EFI_STATUS IsConfigureWholeSocket( IN UINT32 DimmsNum, IN UINT32 SocketId, OUT BOOLEAN *pWholeSocket ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; UINT32 DimmsOnSocket = 0; NVDIMM_ENTRY(); if (pWholeSocket == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pWholeSocket = FALSE; LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (SocketId == pDimm->SocketId && IsDimmManageable(pDimm)) { DimmsOnSocket++; } } if (DimmsNum == DimmsOnSocket) { *pWholeSocket = TRUE; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** For a given set of Region goal dimms reduce the capacity of the Region based on the requested reserved size @param[in out] pReservedSize Size to reduce the Region capacity @param[in out] RegionGoalDimms Array of Region goal dimms to reduce @param[in out] pRegionGoalDimmsNum number of elements in the array @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ STATIC EFI_STATUS ReduceAppDirectCapacityPerReservedCapacity( IN OUT UINT64 *pReservedSize, IN OUT REGION_GOAL_DIMM RegionGoalDimms[MAX_DIMMS], IN OUT UINT32 *pRegionGoalDimmsNum ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT64 TotalRegionGoalCapacity = 0; UINT32 Index = 0; UINT32 Index2 = 0; UINT32 TempRegionGoalDimmIndex = 0; UINT64 ReduceBy = 0; UINT32 RemovedRegionGoalDimmsNum = 0; REGION_GOAL_DIMM TempRegionGoalDimms[MAX_DIMMS]; UINT32 StartingRegionGoalDimmsNum = 0; BOOLEAN RemoveAllGoals = TRUE; NVDIMM_ENTRY(); if (pReservedSize == NULL || RegionGoalDimms == NULL || pRegionGoalDimmsNum == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } StartingRegionGoalDimmsNum = *pRegionGoalDimmsNum; ZeroMem(TempRegionGoalDimms, sizeof(TempRegionGoalDimms[0]) * MAX_DIMMS); if (*pReservedSize == 0) { goto Finish; } // If capacity in the dimms is less than the amount requested // then take all capacity in dimms for (Index = 0; Index < *pRegionGoalDimmsNum; Index++) { TotalRegionGoalCapacity += RegionGoalDimms[Index].RegionSize; } if (TotalRegionGoalCapacity <= *pReservedSize) { for (Index = 0; Index < *pRegionGoalDimmsNum; Index++) { *pReservedSize -= RegionGoalDimms[Index].RegionSize; RegionGoalDimms[Index].RegionSize = 0; if (RegionGoalDimms[Index].VolatileSize > 0) { RemoveAllGoals = FALSE; } } if (RemoveAllGoals) { *pRegionGoalDimmsNum = 0; } } else { // When reducing capacity we don't want to stop unless we have consumed all the goals or // we have reduced the requested amount for (Index2 = 0; Index2 < StartingRegionGoalDimmsNum; Index2++) { // If reserved capacity does not consume the dimms then try to reduce each dimm evenly in allocations // of aligned persistent capacity. Region size should already be aligned to RegionAlignment ReduceBy = *pReservedSize / *pRegionGoalDimmsNum; ReduceBy = ROUNDUP(ReduceBy, gNvmDimmData->Alignments.RegionPersistentAlignment); for (Index = 0; Index < *pRegionGoalDimmsNum; Index++) { // reduce each DIMM evenly if possible if (RegionGoalDimms[Index].RegionSize >= ReduceBy && *pReservedSize >= ReduceBy) { RegionGoalDimms[Index].RegionSize -= ReduceBy; *pReservedSize -= ReduceBy; } // reduce little more than needed (because of RegionAlignment) else if (RegionGoalDimms[Index].RegionSize >= ReduceBy && *pReservedSize < ReduceBy) { RegionGoalDimms[Index].RegionSize -= ReduceBy; *pReservedSize = 0; } else { *pReservedSize -= RegionGoalDimms[Index].RegionSize; RegionGoalDimms[Index].RegionSize = 0; } } // Reduce array and remove Dimms that have had all capacity RSVD for (Index = 0; Index < *pRegionGoalDimmsNum; Index++) { if (RegionGoalDimms[Index].RegionSize == 0) { RemovedRegionGoalDimmsNum++; } } if (RemovedRegionGoalDimmsNum != 0) { for (Index = 0, TempRegionGoalDimmIndex = 0; Index < *pRegionGoalDimmsNum; Index++) { if (RegionGoalDimms[Index].RegionSize != 0 || RegionGoalDimms[Index].VolatileSize != 0) { CopyMem_S(&TempRegionGoalDimms[TempRegionGoalDimmIndex], sizeof(TempRegionGoalDimms[Index]), &RegionGoalDimms[Index], sizeof(TempRegionGoalDimms[Index])); TempRegionGoalDimmIndex++; } } ZeroMem(RegionGoalDimms, sizeof(RegionGoalDimms[0]) * MAX_DIMMS); CopyMem_S(RegionGoalDimms, sizeof(RegionGoalDimms[0]) * MAX_DIMMS, TempRegionGoalDimms, sizeof(RegionGoalDimms[0]) * MAX_DIMMS); *pRegionGoalDimmsNum = TempRegionGoalDimmIndex; } // Recalculate Total remaining capacity for next loop for (Index = 0; Index < *pRegionGoalDimmsNum; Index++) { TotalRegionGoalCapacity += RegionGoalDimms[Index].RegionSize; } if (TotalRegionGoalCapacity == 0 || *pReservedSize == 0 || *pRegionGoalDimmsNum == 0) { break; } } if (TotalRegionGoalCapacity > 0 && *pReservedSize > 0) { ReturnCode = EFI_DEVICE_ERROR; NVDIMM_DBG("Unable to correctly map reserved capacity"); } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Map specified request to actual Region Goal templates. Resolve special "remaining" values. @param[in] pDimms Array of pointers to manageable DIMMs only @param[in] pDimmsNum Number of pointers in pDimms @param[out] DimmsSymmetrical Array of Dimms for symmetrical region config @param[out] pDimmsSymmetricalNum Returned number of items in DimmsSymmetrical @param[out] DimmsAsymmetrical Array of Dimms for asymmetrical region config @param[out] pDimmsAsymmetricalNum Returned number of items in DimmsAsymmetrical @param[in] PersistentMemType Persistent memory type @param[in] VolatileSize Volatile region size @param[in] ReservedPercent Amount of AppDirect memory to not map in percent @param[in] pMaxPMInterleaveSets Pointer to MaxPmInterleaveSets per Die & per Dcpmm @param[out] pVolatileSizeActual Actual Volatile region size @param[out] RegionGoalTemplates Array of region goal templates @param[out] pRegionGoalTemplatesNum Number of items in RegionGoalTemplates @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS MapRequestToActualRegionGoalTemplates( IN DIMM *pDimms[MAX_DIMMS], IN UINT32 DimmsNum, OUT REGION_GOAL_DIMM DimmsSymmetrical[MAX_DIMMS], OUT UINT32 *pDimmsSymmetricalNum, OUT REGION_GOAL_DIMM DimmsAsymmetrical[MAX_DIMMS], OUT UINT32 *pDimmsAsymmetricalNum, IN UINT8 PersistentMemType, IN UINT64 VolatileSize, IN UINT32 ReservedPercent, IN MAX_PMINTERLEAVE_SETS *pMaxPMInterleaveSets, OUT UINT64 *pVolatileSizeActual OPTIONAL, OUT REGION_GOAL_TEMPLATE RegionGoalTemplates[MAX_IS_PER_DIMM], OUT UINT32 *pRegionGoalTemplatesNum, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; UINT64 AvailablePersistentSize = 0; UINT64 VolatileSizeActualOnDimm = 0; UINT64 SymmetricalSize = 0; UINT64 LeastDimmSize = MAX_UINT64_VALUE; UINT64 LeastPersistentSize = MAX_UINT64_VALUE; UINT64 PersistentSizeAsym = 0; UINT64 VolatileSizeActual = 0; UINT64 RegionPersistentAlignment = gNvmDimmData->Alignments.RegionPersistentAlignment; UINT64 TotalRawCapacity = 0; BOOLEAN AllReserved = FALSE; UINT64 ReservedSize = 0; UINT64 UnAllocatedAppDirect = 0; UINT32 AppDirectRegionGoalDimmsNum = 0; NVDIMM_ENTRY(); if (pDimms == NULL || DimmsNum == 0 || DimmsSymmetrical == NULL || pDimmsSymmetricalNum == NULL || DimmsAsymmetrical == NULL || pDimmsAsymmetricalNum == NULL || RegionGoalTemplates == NULL || pRegionGoalTemplatesNum == NULL || pCommandStatus == NULL) { goto Finish; } *pDimmsSymmetricalNum = 0; *pDimmsAsymmetricalNum = 0; /** there can be different sized dimms within the same socket **/ for (Index = 0; Index < DimmsNum; Index++) { if (LeastDimmSize > pDimms[Index]->RawCapacity) { LeastDimmSize = pDimms[Index]->RawCapacity; } TotalRawCapacity += pDimms[Index]->RawCapacity; } if (TotalRawCapacity == ReservedSize) { AllReserved = TRUE; } if (TotalRawCapacity <= VolatileSize) { *pRegionGoalTemplatesNum = 0; VolatileSizeActual = 0; for (Index = 0; Index < DimmsNum; Index++) { VolatileSizeActual += pDimms[Index]->RawCapacity; DimmsSymmetrical[Index].pDimm = pDimms[Index]; DimmsSymmetrical[Index].VolatileSize = pDimms[Index]->RawCapacity; (*pDimmsSymmetricalNum)++; } } else if (VolatileSize > 0 && PersistentMemType == PM_TYPE_RESERVED) { SymmetricalSize = ROUNDDOWN(LeastDimmSize, gNvmDimmData->Alignments.RegionVolatileAlignment) * DimmsNum; ReturnCode = CalculateActualVolatileSize(LeastDimmSize, VolatileSize / DimmsNum, &VolatileSizeActualOnDimm); if (EFI_ERROR(ReturnCode)) { goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { VolatileSizeActual += VolatileSizeActualOnDimm; DimmsSymmetrical[Index].pDimm = pDimms[Index]; DimmsSymmetrical[Index].VolatileSize = VolatileSizeActualOnDimm; (*pDimmsSymmetricalNum)++; } } else { SymmetricalSize = ROUNDDOWN(LeastDimmSize, gNvmDimmData->Alignments.RegionVolatileAlignment) * DimmsNum; ReturnCode = CalculateActualVolatileSize(LeastDimmSize, VolatileSize / DimmsNum, &VolatileSizeActualOnDimm); if (EFI_ERROR(ReturnCode)) { goto Finish; } // Asymmetrical dimm configuration, find the partition alignment // point such that some amount of persistence exists on every dimm if (VolatileSizeActualOnDimm * DimmsNum >= SymmetricalSize) { if (LeastDimmSize < gNvmDimmData->Alignments.RegionPartitionAlignment) { ReturnCode = EFI_ABORTED; ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } ReturnCode = CalculateActualVolatileSize(LeastDimmSize, LeastDimmSize - gNvmDimmData->Alignments.RegionPartitionAlignment, &VolatileSizeActualOnDimm); if (EFI_ERROR(ReturnCode)) { goto Finish; } } if (PersistentMemType != PM_TYPE_RESERVED) { LeastPersistentSize = MAX_UINT64_VALUE; /** Calculate available persistent memory for Interleave Sets **/ for (Index = 0; Index < DimmsNum; Index++) { // Dimms within the same socket can have different capacities if (LeastPersistentSize > (pDimms[Index]->RawCapacity - VolatileSizeActualOnDimm)) { LeastPersistentSize = pDimms[Index]->RawCapacity - VolatileSizeActualOnDimm; LeastPersistentSize = ROUNDDOWN(LeastPersistentSize, RegionPersistentAlignment); } } for (Index = 0; Index < DimmsNum; Index++) { AvailablePersistentSize = pDimms[Index]->RawCapacity - VolatileSizeActualOnDimm; AvailablePersistentSize = ROUNDDOWN(AvailablePersistentSize, RegionPersistentAlignment); if (AvailablePersistentSize == 0) { ResetCmdStatus(pCommandStatus, NVM_ERR_PERS_MEM_MUST_BE_APPLIED_TO_ALL_DIMMS); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } DimmsSymmetrical[Index].pDimm = pDimms[Index]; if (PersistentMemType == PM_TYPE_AD_NI) { if (pMaxPMInterleaveSets != NULL) { if (*pDimmsSymmetricalNum >= pMaxPMInterleaveSets->MaxInterleaveSetsSplit.PerDie) { DimmsSymmetrical[Index].RegionSize = 0; DimmsSymmetrical[Index].VolatileSize = VolatileSizeActualOnDimm; SetCmdStatus(pCommandStatus, NVM_WARN_REGION_MAX_PM_INTERLEAVE_SETS_EXCEEDED); } else { DimmsSymmetrical[Index].RegionSize = AvailablePersistentSize; DimmsSymmetrical[Index].VolatileSize = VolatileSizeActualOnDimm; AppDirectRegionGoalDimmsNum++; } } else { DimmsSymmetrical[Index].RegionSize = AvailablePersistentSize; DimmsSymmetrical[Index].VolatileSize = VolatileSizeActualOnDimm; } } else if (PersistentMemType == PM_TYPE_AD) { DimmsSymmetrical[Index].RegionSize = LeastPersistentSize; DimmsSymmetrical[Index].VolatileSize = VolatileSizeActualOnDimm; PersistentSizeAsym = AvailablePersistentSize - LeastPersistentSize; if ((PersistentSizeAsym > 0) && (pMaxPMInterleaveSets != NULL)) { if (pMaxPMInterleaveSets->MaxInterleaveSetsSplit.PerDcpmm == MAX_IS_PER_DIMM) { if ((*pDimmsAsymmetricalNum + 1) < pMaxPMInterleaveSets->MaxInterleaveSetsSplit.PerDie) { DimmsAsymmetrical[*pDimmsAsymmetricalNum].pDimm = pDimms[Index]; DimmsAsymmetrical[*pDimmsAsymmetricalNum].RegionSize = PersistentSizeAsym; (*pDimmsAsymmetricalNum)++; } else { SetCmdStatus(pCommandStatus, NVM_WARN_REGION_MAX_PM_INTERLEAVE_SETS_EXCEEDED); } } } else if (PersistentSizeAsym > 0) { DimmsAsymmetrical[*pDimmsAsymmetricalNum].pDimm = pDimms[Index]; DimmsAsymmetrical[*pDimmsAsymmetricalNum].RegionSize = PersistentSizeAsym; (*pDimmsAsymmetricalNum)++; } } (*pDimmsSymmetricalNum)++; } /** Calculate Reserved size **/ ReturnCode = CalculateDimmCapacityFromPercent(pDimms, (AppDirectRegionGoalDimmsNum != 0 ? AppDirectRegionGoalDimmsNum: *pDimmsSymmetricalNum), ReservedPercent, &ReservedSize); if (EFI_ERROR(ReturnCode)) { goto Finish; } //If VolatileCapacity is zero there will be some unallocated PM capacity already due to AppDirect alignments //Remove this from ReservedSize as it is already reserved and does not need to be removed from AppDirect if (VolatileSize == 0) { for (Index = 0; Index < DimmsNum; Index++) { UnAllocatedAppDirect = (pDimms[Index]->RawCapacity - ROUNDDOWN(pDimms[Index]->RawCapacity, gNvmDimmData->Alignments.RegionPersistentAlignment)); if (ReservedSize >= UnAllocatedAppDirect) { ReservedSize -= UnAllocatedAppDirect; } else { ReservedSize = 0; } } } //Reduce Asym Dimms of RSVD capacity first if (ReservedSize != 0 && *pDimmsAsymmetricalNum != 0) { ReturnCode = ReduceAppDirectCapacityPerReservedCapacity(&ReservedSize, DimmsAsymmetrical, pDimmsAsymmetricalNum); if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } } //If Rsvd capacity still exists reduce symmetrical dimms if (ReservedSize != 0 && *pDimmsSymmetricalNum != 0) { ReturnCode = ReduceAppDirectCapacityPerReservedCapacity(&ReservedSize, DimmsSymmetrical, pDimmsSymmetricalNum); if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } } //User requested all persistent memory but requested some amount not be reserved, // and we ended up reserving it all. Add some small amount back in if (VolatileSize == 0 && !AllReserved && *pDimmsSymmetricalNum == 0) { for (Index = 0; Index < DimmsNum; Index++) { DimmsSymmetrical[Index].pDimm = pDimms[Index]; DimmsSymmetrical[Index].VolatileSize = 0; DimmsSymmetrical[Index].RegionSize = gNvmDimmData->Alignments.RegionPersistentAlignment; (*pDimmsSymmetricalNum)++; } } } // We have reserved all PM capacity and no volatile capacity exists if (PersistentMemType == PM_TYPE_RESERVED || *pDimmsSymmetricalNum == 0) { for (Index = 0; Index < DimmsNum; Index++) { DimmsSymmetrical[Index].pDimm = pDimms[Index]; DimmsSymmetrical[Index].RegionSize = 0; DimmsSymmetrical[Index].VolatileSize = 0; (*pDimmsSymmetricalNum)++; } *pRegionGoalTemplatesNum = 0; } else if (PersistentMemType == PM_TYPE_AD_NI) { RegionGoalTemplates[0].InterleaveSetType = NON_INTERLEAVED; RegionGoalTemplates[0].Asymmetrical = FALSE; *pRegionGoalTemplatesNum = 1; } else if (PersistentMemType == PM_TYPE_AD) { /** Addressing the corner case where first socket has mixed * dimms and the 2nd doesn't - leave the goal template unchanged**/ if (*pRegionGoalTemplatesNum != 2) { RegionGoalTemplates[0].InterleaveSetType = INTERLEAVED; RegionGoalTemplates[0].Asymmetrical = FALSE; *pRegionGoalTemplatesNum = 1; if (*pDimmsAsymmetricalNum > 0) { RegionGoalTemplates[1].InterleaveSetType = NON_INTERLEAVED; RegionGoalTemplates[1].Asymmetrical = TRUE; *pRegionGoalTemplatesNum = 2; } } } else { ReturnCode = EFI_ABORTED; ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } if (VolatileSize > 0) { VolatileSizeActual = VolatileSizeActualOnDimm * DimmsNum; } } /** Check if platform allows volatile mode */ if (VolatileSize > 0) { if (!gNvmDimmData->PMEMDev.IsMemModeAllowedByBios) { SetCmdStatus(pCommandStatus, NVM_WARN_IMC_DDR_PMM_NOT_PAIRED); } else { ReturnCode = SetObjStatusForPMemNotPairedWithDdr(pDimms, DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("SetObjStatusForPMemNotPairedWithDdr failed."); goto Finish; } } } if (pVolatileSizeActual != NULL) { *pVolatileSizeActual = VolatileSizeActual; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Verify that all the unconfigured DIMMs or all DIMMs on a given socket are configured at once to keep supported region configs. @param[in] pDimms List of DIMMs to configure @param[in] DimmsNum Number of DIMMs to configure @param[in] PersistentMemType Persistent memory type @param[in] VolatilePercent Volatile region size in percents @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER if one or more parameters are NULL @retval EFI_UNSUPPORTED A given config is unsupported **/ EFI_STATUS VerifyCreatingSupportedRegionConfigs( IN DIMM *pDimms[], IN UINT32 DimmsNum, IN UINT8 PersistentMemType, IN UINT32 VolatilePercent, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS Rc = EFI_SUCCESS; UINT32 ConfiguredDimmsNum = 0; UINT32 UnconfiguredDimmsNum = 0; UINT32 SpecifiedConfiguredDimmsNum = 0; UINT32 SpecifiedUnconfiguredDimmsNum = 0; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; UINT32 Socket = 0; UINT32 Index = 0; UINT32 ExistingADNonInterleavedRegions = 0; UINT32 ExistingVolatileRegions = 0; UINT32 NumOfDimmsOnSocket = 0; NVDIMM_ENTRY(); if (pDimms == NULL || pCommandStatus == NULL) { Rc = EFI_INVALID_PARAMETER; goto Finish; } for (Socket = 0; Socket < MAX_SOCKETS; Socket++) { ConfiguredDimmsNum = 0; UnconfiguredDimmsNum = 0; NumOfDimmsOnSocket = 0; ExistingADNonInterleavedRegions = 0; ExistingVolatileRegions = 0; /** Get a number of all configured and unconfigured Dimms on a given socket **/ LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (Socket == pDimm->SocketId) { // Unmanageable, non-functional, and media inaccessible DCPMMs are not included in goal requests if (!IsDimmManageable(pDimm) || pDimm->NonFunctional || DIMM_MEDIA_NOT_ACCESSIBLE(pDimm->BootStatusBitmask)) { continue; } // Population Violation DCPMMs are not included in goal requests except ADx1 100% if (IsDimmInPopulationViolation(pDimm) && !(PM_TYPE_AD_NI == PersistentMemType && 0 == VolatilePercent)) { continue; } if (pDimm->Configured) { ConfiguredDimmsNum += 1; } else { UnconfiguredDimmsNum += 1; } if (!IsPointerInArray((VOID **)pDimms, DimmsNum, pDimm)) { // Calculate the ADx1 regions on the unspecified DIMMs if (pDimm->VolatileCapacity == 0 && pDimm->ISsNfitNum > 0 && pDimm->pISsNfit[APPDIRECT_1_INDEX]->InterleaveFormatWays == INTERLEAVE_WAYS_X1) { ExistingADNonInterleavedRegions++; } // Calculate number of unspecified DIMMs with only volatile capacities if (pDimm->VolatileCapacity > 0 && pDimm->ISsNfitNum == 0) { ExistingVolatileRegions++; } } NumOfDimmsOnSocket++; } } SpecifiedConfiguredDimmsNum = 0; SpecifiedUnconfiguredDimmsNum = 0; /** Get a number of specified configured and unconfigured DIMMs on a given socket **/ for (Index = 0; Index < DimmsNum; Index++) { if (Socket == pDimms[Index]->SocketId) { // Unmanageable and non-functional DCPMMs are not included in goal requests if (!IsDimmManageable(pDimms[Index]) || pDimms[Index]->NonFunctional) { continue; } // Population Violation DCPMMs are not included in goal requests except ADx1 100% if (IsDimmInPopulationViolation(pDimms[Index]) && !(PM_TYPE_AD_NI == PersistentMemType && 0 == VolatilePercent)) { continue; } if (pDimms[Index]->Configured) { SpecifiedConfiguredDimmsNum += 1; } else { SpecifiedUnconfiguredDimmsNum += 1; } } } /** If any DIMM is specified for a given socket then: - Target all unconfigured DCPMMs - Target all DCPMMs on a given socket - Target DCPMMs for AppDirect Not-Interleaved with all unspecified DCPMMs configured for AppDirect Not-Interleaved only - Target DCPMMs for 100% MemoryMode with all unspecified DCPMMs configured for MemoryMode only **/ if (!( (SpecifiedConfiguredDimmsNum == 0 && SpecifiedUnconfiguredDimmsNum == 0) || (SpecifiedConfiguredDimmsNum == 0 && SpecifiedUnconfiguredDimmsNum == UnconfiguredDimmsNum) || (SpecifiedConfiguredDimmsNum == ConfiguredDimmsNum && SpecifiedUnconfiguredDimmsNum == UnconfiguredDimmsNum) || ((PM_TYPE_AD_NI == PersistentMemType || (PM_TYPE_AD == PersistentMemType && (SpecifiedConfiguredDimmsNum + SpecifiedUnconfiguredDimmsNum) == 1)) && VolatilePercent == 0 && ((SpecifiedConfiguredDimmsNum + SpecifiedUnconfiguredDimmsNum + ExistingADNonInterleavedRegions) == NumOfDimmsOnSocket)) || (VolatilePercent == 100 && ((SpecifiedConfiguredDimmsNum + SpecifiedUnconfiguredDimmsNum + ExistingVolatileRegions) == NumOfDimmsOnSocket)) )) { Rc = EFI_UNSUPPORTED; ResetCmdStatus(pCommandStatus, NVM_ERR_REGION_CONF_UNSUPPORTED_CONFIG); goto Finish; } } Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** Retrieve goal configurations by using Platform Config Data @param[in, out] pDimmList Head of the list of all NVM DIMMs in the system @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RetrieveGoalConfigsFromPlatformConfigData( IN OUT LIST_ENTRY *pDimmList, IN BOOLEAN RestoreCorrupt ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS IReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; NVDIMM_CONFIGURATION_HEADER *pPcdConfHeader = NULL; NVDIMM_PLATFORM_CONFIG_INPUT *pPcdConfInput = NULL; NVDIMM_PLATFORM_CONFIG_OUTPUT *pPcdConfOutput = NULL; NVDIMM_PARTITION_SIZE_CHANGE *pPartitionSizeChange = NULL; PCAT_TABLE_HEADER *pPcatTable = NULL; UINT32 SizeOfPcatTables = 0; REGION_GOAL *pRegionGoals[MAX_IS_PER_DIMM * MAX_DIMMS]; UINT32 RegionGoalsNum = 0; REGION_GOAL *pNewRegionGoal = NULL; BOOLEAN New = FALSE; UINT32 SequenceIndex = 0; ACPI_REVISION PcdCinRev; UINT8 InterleaveChangeStatus = 0; NVDIMM_ENTRY(); SetMem(pRegionGoals, sizeof(pRegionGoals), 0x0); if (pDimmList == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = ClearInternalGoalConfigsInfo(pDimmList); if (EFI_ERROR(ReturnCode)) { goto FinishError; } LIST_FOR_EACH(pDimmNode, pDimmList) { pDimm = DIMM_FROM_NODE(pDimmNode); // Set default values pDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_NO_GOAL_OR_SUCCESS; pDimm->RegionsGoalConfig = FALSE; pDimm->PcdSynced = TRUE; if (!IsDimmManageable(pDimm) || DIMM_MEDIA_NOT_ACCESSIBLE(pDimm->BootStatusBitmask)) { continue; } // Free previous use of pcd header if needed FREE_POOL_SAFE(pPcdConfHeader); ReturnCode = GetPlatformConfigDataOemPartition(pDimm, RestoreCorrupt, &pPcdConfHeader); #ifdef MEMORY_CORRUPTION_WA if (ReturnCode == EFI_DEVICE_ERROR) { ReturnCode = GetPlatformConfigDataOemPartition(pDimm, RestoreCorrupt, &pPcdConfHeader); } #endif // MEMORY_CORRUPTIO_WA if (EFI_ERROR(ReturnCode)) { // Ignore all errors except for PMem module busy with sanitize operation if (EFI_NO_RESPONSE == ReturnCode) { /* Save the return code here and continue with the execution for rest of the dimms. This is done to make the UEFI initialization succeed. During UEFI init, return code will be ignored but we have to error out when the actual command is executed. */ IReturnCode = ReturnCode; } ReturnCode = EFI_SUCCESS; continue; } if (NULL != pPcdConfHeader) { pPcdConfInput = GET_NVDIMM_PLATFORM_CONFIG_INPUT(pPcdConfHeader); pPcdConfOutput = GET_NVDIMM_PLATFORM_CONFIG_OUTPUT(pPcdConfHeader); } // If no PCD Header, CIN record then no goal if ((NULL == pPcdConfHeader) || (pPcdConfHeader->ConfInputStartOffset == 0) || (pPcdConfHeader->ConfInputDataSize == 0)) { NVDIMM_DBG("There is no Config Input table"); continue; } // CIN is corrupt else if (!IsPcdConfInputHeaderValid(pPcdConfInput, pDimm->PcdOemPartitionSize)) { pPcdConfHeader->ConfInputStartOffset = 0; pPcdConfHeader->ConfInputDataSize = 0; NVDIMM_DBG("The Config Input table is corrupted, Ignoring it"); continue; } // If CIN and COUT sequence are the same, then goal attempted to be applied already else if ((pPcdConfHeader->ConfOutputStartOffset != 0) && (pPcdConfHeader->ConfOutputDataSize != 0) && IsPcdConfOutputHeaderValid(pPcdConfOutput, pDimm->PcdOemPartitionSize) && (pPcdConfInput->SequenceNumber == pPcdConfOutput->SequenceNumber)) { NVDIMM_DBG("The config goal is already applied"); continue; } // We have a valid goal after this point PcdCinRev = pPcdConfInput->Header.Revision; pDimm->PcdSynced = FALSE; pDimm->RegionsGoalConfig = TRUE; pPcatTable = (PCAT_TABLE_HEADER *) &pPcdConfInput->pPcatTables; if (pPcatTable == NULL) { NVDIMM_ERR("pPcatTable is null"); ReturnCode = EFI_ABORTED; goto FinishError; } SizeOfPcatTables = pPcdConfHeader->ConfInputDataSize - (UINT32)((UINT8 *)pPcatTable - (UINT8 *)pPcdConfInput); SequenceIndex = 0; /** Example of the use of the while loop condition Extension table #1 offset: 0 size: 10 Extension table #2 offset: 10 size: 5 Size of extension tables: 15 (10 + 5) Iteration #1: offset: 0 Iteration #2: offset: 10 Iteration #3: offset: 15 stop the loop: offset isn't less than size **/ while ((UINT32) ((UINT8 *)pPcatTable - (UINT8 *) &pPcdConfInput->pPcatTables) < SizeOfPcatTables) { switch (pPcatTable->Type) { case PCAT_TYPE_PARTITION_SIZE_CHANGE_TABLE: pPartitionSizeChange = (NVDIMM_PARTITION_SIZE_CHANGE *) pPcatTable; pDimm->VolatileSizeGoal = pDimm->RawCapacity - pPartitionSizeChange->PmPartitionSize; break; case PCAT_TYPE_INTERLEAVE_INFORMATION_TABLE: if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcdCinRev)) { NVDIMM_INTERLEAVE_INFORMATION *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION *)pPcatTable; ReturnCode = RetrieveRegionGoalFromInterleaveInformationTable(pRegionGoals, RegionGoalsNum, pInterleaveInfo, PcdCinRev, &pNewRegionGoal, &New); } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdCinRev)) { NVDIMM_INTERLEAVE_INFORMATION3 *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION3 *)pPcatTable; ReturnCode = RetrieveRegionGoalFromInterleaveInformationTable(pRegionGoals, RegionGoalsNum, pInterleaveInfo, PcdCinRev, &pNewRegionGoal, &New); } if (ReturnCode == EFI_NOT_FOUND) { ReturnCode = EFI_SUCCESS; break; } else if (EFI_ERROR(ReturnCode)) { goto FinishError; } if (New && pNewRegionGoal != NULL) { pNewRegionGoal->SequenceIndex = SequenceIndex; ASSERT(RegionGoalsNum < MAX_IS_PER_DIMM * MAX_DIMMS); pRegionGoals[RegionGoalsNum] = pNewRegionGoal; RegionGoalsNum++; } SequenceIndex++; break; case PCAT_TYPE_CONFIG_MANAGEMENT_ATTRIBUTES_TABLE: break; default: NVDIMM_DBG("This type (%d) of PCAT table shouldn't be contained in Config Input table", pPcatTable->Type); ReturnCode = EFI_ABORTED; goto FinishError; break; } pPcatTable = (PCAT_TABLE_HEADER *)((UINT8 *)pPcatTable + pPcatTable->Length); } /** Unknown status as default **/ pDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_UNKNOWN; if (pPcdConfHeader->ConfOutputStartOffset == 0 || pPcdConfHeader->ConfOutputDataSize == 0 || pPcdConfInput->SequenceNumber != pPcdConfOutput->SequenceNumber) { pDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_NEW; pDimm->PcdSynced = TRUE; NVDIMM_DBG("There is no Config Output table"); FREE_POOL_SAFE(pPcdConfHeader); continue; } if (!IsPcdConfOutputHeaderValid(pPcdConfOutput, pDimm->PcdOemPartitionSize)) { ReturnCode = EFI_ABORTED; goto FinishError; } if (pPcdConfOutput->ValidationStatus == CONFIG_OUTPUT_STATUS_SUCCESS) { pDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_NO_GOAL_OR_SUCCESS; pDimm->PcdSynced = TRUE; FREE_POOL_SAFE(pPcdConfHeader); continue; } else if (pPcdConfOutput->ValidationStatus == CONFIG_OUTPUT_STATUS_CPU_MAX_MEMORY_LIMIT_VIOLATION || pPcdConfOutput->ValidationStatus == CONFIG_OUTPUT_STATUS_NM_FM_RATIO_UNSUPPORTED || pPcdConfOutput->ValidationStatus == CONFIG_OUTPUT_STATUS_POPULATION_ISSUE) { pDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_BAD_REQUEST; } pPcatTable = (PCAT_TABLE_HEADER *) &pPcdConfOutput->pPcatTables; if (pPcatTable == NULL) { NVDIMM_ERR("pPcatTable is null"); ReturnCode = EFI_ABORTED; goto FinishError; } SizeOfPcatTables = pPcdConfHeader->ConfOutputDataSize - (UINT32)((UINT8 *)pPcatTable - (UINT8 *)pPcdConfOutput); /** Example of the use of the while loop condition Extension table #1 offset: 0 size: 10 Extension table #2 offset: 10 size: 5 Size of extension tables: 15 (10 + 5) Iteration #1: offset: 0 Iteration #2: offset: 10 Iteration #3: offset: 15 stop the loop: offset isn't less than size **/ while ((UINT32) ((UINT8 *)pPcatTable - (UINT8 *) &pPcdConfOutput->pPcatTables) < SizeOfPcatTables) { switch (pPcatTable->Type) { case PCAT_TYPE_PARTITION_SIZE_CHANGE_TABLE: pPartitionSizeChange = (NVDIMM_PARTITION_SIZE_CHANGE *) pPcatTable; switch (pPartitionSizeChange->PartitionSizeChangeStatus) { case PARTITION_SIZE_CHANGE_STATUS_SUCCESS: break; case PARTITION_SIZE_CHANGE_STATUS_DIMM_MISSING: case PARTITION_SIZE_CHANGE_STATUS_ISET_MISSING: case PARTITION_SIZE_CHANGE_STATUS_UNSUPPORTED_ALIGNMENT: pDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_BAD_REQUEST; break; case PARTITION_SIZE_CHANGE_STATUS_EXCEED_DRAM_DECODERS: case PARTITION_SIZE_CHANGE_STATUS_EXCEED_SIZE: pDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_NOT_ENOUGH_RESOURCES; break; case PARTITION_SIZE_CHANGE_STATUS_FW_ERROR: pDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_FIRMWARE_ERROR; break; case PARTITION_SIZE_CHANGE_STATUS_RESERVED: case PARTITION_SIZE_CHANGE_STATUS_UNDEFINED: default: pDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_FAILED_UNKNOWN; break; } break; case PCAT_TYPE_INTERLEAVE_INFORMATION_TABLE: if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcdCinRev)) { NVDIMM_INTERLEAVE_INFORMATION *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION *)pPcatTable; InterleaveChangeStatus = pInterleaveInfo->InterleaveChangeStatus; } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdCinRev)) { NVDIMM_INTERLEAVE_INFORMATION3 *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION3 *)pPcatTable; InterleaveChangeStatus = pInterleaveInfo->InterleaveChangeStatus; } switch (InterleaveChangeStatus) { case INTERLEAVE_INFO_STATUS_SUCCESS: break; case INTERLEAVE_INFO_STATUS_DIMM_MISSING: case INTERLEAVE_INFO_STATUS_ISET_MISSING: case INTERLEAVE_INFO_STATUS_CHANNEL_NOT_MATCH: case INTERLEAVE_INFO_STATUS_UNSUPPORTED_ALIGNMENT: case INTERLEAVE_INFO_STATUS_REQUEST_UNSUPPORTED: case INTERLEAVE_INFO_STATUS_CIN_MISSING: pDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_BAD_REQUEST; break; case INTERLEAVE_INFO_STATUS_EXCEED_DRAM_DECODERS: case INTERLEAVE_INFO_STATUS_EXCEED_MAX_SPA_SPACE: pDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_NOT_ENOUGH_RESOURCES; break; case INTERLEAVE_INFO_STATUS_NOT_PROCESSED: case INTERLEAVE_INFO_STATUS_PARTITIONING_FAILED: case INTERLEAVE_INFO_STATUS_UNDEFINED: default : pDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_FAILED_UNKNOWN; break; } break; case PCAT_TYPE_CONFIG_MANAGEMENT_ATTRIBUTES_TABLE: break; default: NVDIMM_DBG("This type (%d) of PCAT table shouldn't be contained in Config Output table", pPcatTable->Type); ReturnCode = EFI_ABORTED; goto FinishError; break; } if (pDimm->GoalConfigStatus != GOAL_CONFIG_STATUS_UNKNOWN) { break; } pPcatTable = (PCAT_TABLE_HEADER *)((UINT8 *)pPcatTable + pPcatTable->Length); } FREE_POOL_SAFE(pPcdConfHeader); pDimm->PcdSynced = TRUE; } /** Success **/ goto Finish; FinishError: ClearInternalGoalConfigsInfo(pDimmList); Finish: FREE_POOL_SAFE(pPcdConfHeader); ReturnCode = (IReturnCode != EFI_SUCCESS) ? IReturnCode : ReturnCode; NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Parse Interleave Information table and retrieve Region Goal (create a Region Goal if it doesn't exist yet) @param[in] pRegionGoals Array of all Region Goals in the system @param[in] RegionGoalsNum Number of pointers in pRegionGoals @param[in] pInterleaveInfo Interleave Information table retrieved from DIMM @param[in] PcdCinRev Revision of the PCD Config Input table @param[out] ppRegionGoal Output variable for Region Goal @param[out] pNew True if Region Goal new created, False if already exists @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RetrieveRegionGoalFromInterleaveInformationTable( IN REGION_GOAL *pRegionGoals[], IN UINT32 RegionGoalsNum, IN VOID *pInterleaveInfoTable, IN ACPI_REVISION PcdCinRev, OUT REGION_GOAL **ppRegionGoal, OUT BOOLEAN *pNew ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; BOOLEAN RegionGoalExists = FALSE; UINT32 Index = 0; REGION_GOAL *pRegionGoal = NULL; NVDIMM_ENTRY(); if (pRegionGoals == NULL || pInterleaveInfoTable == NULL || ppRegionGoal == NULL || pNew == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto FinishError; } if (IS_ACPI_REV_INVALID(PcdCinRev)) { NVDIMM_DBG("Revision of PCD Config Input table is invalid"); ReturnCode = EFI_INVALID_PARAMETER; goto FinishError; } if (IS_ACPI_REV_MAJ_0_MIN_VALID(PcdCinRev)) { NVDIMM_INTERLEAVE_INFORMATION *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION *)pInterleaveInfoTable; NVDIMM_IDENTIFICATION_INFORMATION *pCurrentIdentInfo = NULL; /** Check if Interleave Set already exists for this Interleave Set Index **/ for (Index = 0; Index < RegionGoalsNum; Index++) { if (pRegionGoals[Index]->InterleaveSetIndex == pInterleaveInfo->InterleaveSetIndex) { RegionGoalExists = TRUE; pRegionGoal = pRegionGoals[Index]; break; } } if (!RegionGoalExists) { /** Initialize Region Goal **/ pRegionGoal = AllocateZeroPool(sizeof(*pRegionGoal)); if (pRegionGoal == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto FinishError; } pRegionGoal->InterleaveSetIndex = pInterleaveInfo->InterleaveSetIndex; pRegionGoal->ChannelInterleaving = pInterleaveInfo->InterleaveFormatChannel; pRegionGoal->ImcInterleaving = pInterleaveInfo->InterleaveFormatImc; pRegionGoal->NumOfChannelWays = pInterleaveInfo->InterleaveFormatWays; pRegionGoal->DimmsNum = pInterleaveInfo->NumOfDimmsInInterleaveSet; if (pInterleaveInfo->NumOfDimmsInInterleaveSet == INTERLEAVE_WAYS_X1) { pRegionGoal->InterleaveSetType = NON_INTERLEAVED; } else { pRegionGoal->InterleaveSetType = INTERLEAVED; } pRegionGoal->Size = 0; pCurrentIdentInfo = (NVDIMM_IDENTIFICATION_INFORMATION *)&pInterleaveInfo->pIdentificationInfoList; /** First verify all dimm identifiers before assign Region Goal to Dimms **/ for (Index = 0; Index < pInterleaveInfo->NumOfDimmsInInterleaveSet; Index++) { if (IS_ACPI_REV_MAJ_0_MIN_1(PcdCinRev)) { pDimm = GetDimmBySerialNumber(&gNvmDimmData->PMEMDev.Dimms, pCurrentIdentInfo->DimmIdentification.Version1.DimmSerialNumber); } else { pDimm = GetDimmByUniqueIdentifier(&gNvmDimmData->PMEMDev.Dimms, pCurrentIdentInfo->DimmIdentification.Version2.Uid); } if (pDimm == NULL) { ReturnCode = EFI_NOT_FOUND; goto FinishError; } pCurrentIdentInfo++; } pCurrentIdentInfo = (NVDIMM_IDENTIFICATION_INFORMATION *)&pInterleaveInfo->pIdentificationInfoList; /** Now we are sure that all dimm identifiers are valid **/ for (Index = 0; Index < pInterleaveInfo->NumOfDimmsInInterleaveSet; Index++) { if (IS_ACPI_REV_MAJ_0_MIN_1(PcdCinRev)) { pDimm = GetDimmBySerialNumber(&gNvmDimmData->PMEMDev.Dimms, pCurrentIdentInfo->DimmIdentification.Version1.DimmSerialNumber); } else { pDimm = GetDimmByUniqueIdentifier(&gNvmDimmData->PMEMDev.Dimms, pCurrentIdentInfo->DimmIdentification.Version2.Uid); } if (pDimm == NULL) { continue; } pRegionGoal->pDimms[Index] = pDimm; pRegionGoal->Size += pCurrentIdentInfo->PmPartitionSize; ASSERT(pDimm->RegionsGoalNum < MAX_IS_PER_DIMM); pDimm->pRegionsGoal[pDimm->RegionsGoalNum] = pRegionGoal; pDimm->RegionsGoalNum++; pCurrentIdentInfo++; } *pNew = TRUE; } else { *pNew = FALSE; } } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcdCinRev)) { NVDIMM_INTERLEAVE_INFORMATION3 *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION3 *)pInterleaveInfoTable; NVDIMM_IDENTIFICATION_INFORMATION3 *pCurrentIdentInfo = NULL; /** Check if Interleave Set already exists for this Interleave Set Index **/ for (Index = 0; Index < RegionGoalsNum; Index++) { if (pRegionGoals[Index]->InterleaveSetIndex == pInterleaveInfo->InterleaveSetIndex) { RegionGoalExists = TRUE; pRegionGoal = pRegionGoals[Index]; break; } } if (!RegionGoalExists) { /** Initialize Region Goal **/ pRegionGoal = AllocateZeroPool(sizeof(*pRegionGoal)); if (pRegionGoal == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto FinishError; } pRegionGoal->InterleaveSetIndex = pInterleaveInfo->InterleaveSetIndex; pRegionGoal->ChannelInterleaving = pInterleaveInfo->InterleaveFormatChannel; pRegionGoal->ImcInterleaving = pInterleaveInfo->InterleaveFormatImc; GetBitFieldForNumOfChannelWays(pInterleaveInfo->NumOfDimmsInInterleaveSet, &pRegionGoal->NumOfChannelWays); pRegionGoal->DimmsNum = pInterleaveInfo->NumOfDimmsInInterleaveSet; if (pInterleaveInfo->NumOfDimmsInInterleaveSet == INTERLEAVE_WAYS_X1) { pRegionGoal->InterleaveSetType = NON_INTERLEAVED; } else { pRegionGoal->InterleaveSetType = INTERLEAVED; } pRegionGoal->Size = 0; pCurrentIdentInfo = (NVDIMM_IDENTIFICATION_INFORMATION3 *)&pInterleaveInfo->pIdentificationInfoList; /** First verify all dimm identifiers before assign Region Goal to Dimms **/ for (Index = 0; Index < pInterleaveInfo->NumOfDimmsInInterleaveSet; Index++) { pDimm = GetDimmByUniqueIdentifier(&gNvmDimmData->PMEMDev.Dimms, pCurrentIdentInfo->DimmIdentification); if (pDimm == NULL) { ReturnCode = EFI_NOT_FOUND; goto FinishError; } pCurrentIdentInfo++; } pCurrentIdentInfo = (NVDIMM_IDENTIFICATION_INFORMATION3 *)&pInterleaveInfo->pIdentificationInfoList; /** Now we are sure that all dimm identifiers are valid **/ for (Index = 0; Index < pInterleaveInfo->NumOfDimmsInInterleaveSet; Index++) { pDimm = GetDimmByUniqueIdentifier(&gNvmDimmData->PMEMDev.Dimms, pCurrentIdentInfo->DimmIdentification); if (pDimm == NULL) { continue; } pRegionGoal->pDimms[Index] = pDimm; pRegionGoal->Size += pCurrentIdentInfo->PmPartitionSize; ASSERT(pDimm->RegionsGoalNum < MAX_IS_PER_DIMM); pDimm->pRegionsGoal[pDimm->RegionsGoalNum] = pRegionGoal; pDimm->RegionsGoalNum++; pCurrentIdentInfo++; } *pNew = TRUE; } else { *pNew = FALSE; } } /** Success **/ *ppRegionGoal = pRegionGoal; goto Finish; FinishError: if (!RegionGoalExists) { FREE_POOL_SAFE(pRegionGoal); } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Map Regions Goal configs on specified DIMMs @param[in] DimmsSym Array of Dimms for symmetrical region config @param[in] DimmsSymNum Number of items in DimmsSym @param[in] DimmsAsym Array of Dimms for asymmetrical region config @param[in] DimmsAsymNum Number of items in DimmsAsym @param[in, out] pReserveDimm Dimm that its whole capacity will be set as persistent partition @param[in] ReserveDimmType Type of reserve dimm @param[in] VolatileSize Volatile region size in bytes @param[in] RegionGoalTemplates Array of template goal REGIONs @param[in] RegionGoalTemplatesNum Number of elements in RegionGoalTemplates @param[in] pDriverPreferences Driver preferences for interleave sets @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL or input DIMMs are different than affected DIMMs **/ EFI_STATUS MapRegionsGoal( IN REGION_GOAL_DIMM DimmsSym[MAX_DIMMS], IN UINT32 DimmsSymNum, IN REGION_GOAL_DIMM DimmsAsym[MAX_DIMMS], IN UINT32 DimmsAsymNum, IN OUT DIMM *pReserveDimm OPTIONAL, IN UINT8 ReserveDimmType OPTIONAL, IN UINT64 VolatileSize, IN REGION_GOAL_TEMPLATE RegionGoalTemplates[MAX_IS_PER_DIMM], IN UINT32 RegionGoalTemplatesNum, IN DRIVER_PREFERENCES *pDriverPreferences, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LIST_ENTRY *pDimmNode = NULL; DIMM *pDimm = NULL; DIMM *pDimmsAndReserveDimm[MAX_DIMMS]; UINT32 DimmsAndReserveDimmNum = 0; DIMM *pRelatedDimms[MAX_DIMMS]; UINT32 RelatedDimmsNum = 0; DIMM *pRelatedDimmsOnSocket[MAX_DIMMS_PER_SOCKET]; UINT32 RelatedDimmsOnSocketNum = 0; DIMM *pSpecifiedDimmsOnSocket[MAX_DIMMS_PER_SOCKET]; UINT64 SpecifiedDimmsOnSocketRegionSize[MAX_DIMMS_PER_SOCKET]; UINT32 SpecifiedDimmsOnSocketNum = 0; DIMM *pSpecifiedDimmsOnSocketAsym[MAX_DIMMS_PER_SOCKET]; UINT64 SpecifiedDimmsOnSocketAsymRegionSize[MAX_DIMMS_PER_SOCKET]; UINT32 SpecifiedDimmsOnSocketAsymNum = 0; REGION_GOAL *pExistingRegionsGoal[MAX_IS_CONFIGS]; UINT32 ExistingRegionsGoalNum = 0; REGION_GOAL *pNewRegionsGoal[MAX_IS_PER_SOCKET]; REGION_GOAL *pReserveDimmRegionsGoal = NULL; REGION_GOAL_TEMPLATE ResDimmRegionGoalTemplate; UINT32 NewRegionsGoalNum = 0; UINT32 Socket = 0; UINT32 Index = 0; UINT32 Index2 = 0; UINT32 RegionGoalSequenceIndex = 0; UINT64 InterleaveSetSize = 0; UINT16 AvailableISIndex = 0; REGION_GOAL_DIMM *pRegionGoalDimm = NULL; UINT32 ExistingRegionsNumOnSocket = 0; ACPI_REVISION PcatRevision; BOOLEAN WholeSocket = FALSE; MAX_PMINTERLEAVE_SETS MaxPMInterleaveSets; LIST_ENTRY *pRegionList = NULL; LIST_ENTRY *pRegionNode = NULL; NVM_IS *pRegion = NULL; NVDIMM_ENTRY(); ZeroMem(pDimmsAndReserveDimm, sizeof(pDimmsAndReserveDimm)); ZeroMem(pRelatedDimms, sizeof(pRelatedDimms)); ZeroMem(pRelatedDimmsOnSocket, sizeof(pRelatedDimmsOnSocket)); ZeroMem(pSpecifiedDimmsOnSocket, sizeof(pSpecifiedDimmsOnSocket)); ZeroMem(pExistingRegionsGoal, sizeof(pExistingRegionsGoal)); ZeroMem(pNewRegionsGoal, sizeof(pNewRegionsGoal)); ZeroMem(&ResDimmRegionGoalTemplate, sizeof(ResDimmRegionGoalTemplate)); ZeroMem(&PcatRevision, sizeof(PcatRevision)); ZeroMem(&MaxPMInterleaveSets, sizeof(MaxPMInterleaveSets)); if (DimmsSym == NULL || DimmsAsym == NULL || RegionGoalTemplates == NULL || pCommandStatus == NULL || pDriverPreferences == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (pReserveDimm != NULL && ReserveDimmType != RESERVE_DIMM_AD_NOT_INTERLEAVED) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead != NULL) { PcatRevision.AsUint8 = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr->Header.Revision.AsUint8; } if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) { ReturnCode = RetrieveMaxPMInterleaveSets(&MaxPMInterleaveSets); if (EFI_ERROR(ReturnCode)) { goto Finish; } } /** Get the largest interleave set index in existing Config Input tables on DIMMs. **/ LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (!IsDimmManageable(pDimm)|| !IsDimmInSupportedConfig(pDimm)) { continue; } for (Index = 0; Index < pDimm->RegionsGoalNum; Index++) { if (pDimm->pRegionsGoal[Index]->InterleaveSetIndex > AvailableISIndex) { AvailableISIndex = pDimm->pRegionsGoal[Index]->InterleaveSetIndex; } } } /** Ensure index is unique across all dimms in the system, not just dimms targeted for goal creation. This can happen when adding new dimms to the system with previously configured regions. **/ if (0x0 == AvailableISIndex) { ReturnCode = GetRegionList(&pRegionList, FALSE); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to retrieve the region list."); goto Finish; } /** Get the largest interleave set index in existing regions on DIMMs. **/ LIST_FOR_EACH(pRegionNode, pRegionList) { pRegion = IS_FROM_NODE(pRegionNode); if (pRegion->InterleaveSetIndex > AvailableISIndex) { AvailableISIndex = pRegion->InterleaveSetIndex; } } } /** We have found the largest index. The next one is available. **/ AvailableISIndex++; /** Put all specified Dimms into one array **/ DimmsAndReserveDimmNum = 0; if (DimmsSymNum > 0) { for (Index = 0; Index < DimmsSymNum; Index++) { pDimmsAndReserveDimm[Index] = DimmsSym[Index].pDimm; } DimmsAndReserveDimmNum += DimmsSymNum; } if (pReserveDimm != NULL) { pDimmsAndReserveDimm[DimmsAndReserveDimmNum] = pReserveDimm; DimmsAndReserveDimmNum++; } if (DimmsAndReserveDimmNum == 0) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = ValidateRegionsCorrelations(pDimmsAndReserveDimm, DimmsAndReserveDimmNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = FindRelatedDimmsByRegionGoalConfigs(pDimmsAndReserveDimm, DimmsAndReserveDimmNum, pRelatedDimms, &RelatedDimmsNum); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Get current list of Regions Goal that will be freed at the end. **/ ReturnCode = FindUniqueRegionsGoal(pRelatedDimms, RelatedDimmsNum, pExistingRegionsGoal, &ExistingRegionsGoalNum); if (EFI_ERROR(ReturnCode)) { goto Finish; } for (Socket = 0; Socket < MAX_SOCKETS; Socket++) { RelatedDimmsOnSocketNum = 0; SpecifiedDimmsOnSocketNum = 0; SpecifiedDimmsOnSocketAsymNum = 0; for (Index = 0; Index < RelatedDimmsNum; Index++) { if (Socket == pRelatedDimms[Index]->SocketId && pRelatedDimms[Index] != pReserveDimm) { pRelatedDimmsOnSocket[RelatedDimmsOnSocketNum] = pRelatedDimms[Index]; RelatedDimmsOnSocketNum++; /** Get array of specified DIMMs by user for a given socket **/ pRegionGoalDimm = FindRegionGoalDimm(DimmsSym, DimmsSymNum, pRelatedDimms[Index]); if (pRegionGoalDimm != NULL) { pSpecifiedDimmsOnSocket[SpecifiedDimmsOnSocketNum] = pRegionGoalDimm->pDimm; SpecifiedDimmsOnSocketRegionSize[SpecifiedDimmsOnSocketNum] = pRegionGoalDimm->RegionSize; SpecifiedDimmsOnSocketNum++; } pRegionGoalDimm = FindRegionGoalDimm(DimmsAsym, DimmsAsymNum, pRelatedDimms[Index]); if (pRegionGoalDimm != NULL) { pSpecifiedDimmsOnSocketAsym[SpecifiedDimmsOnSocketAsymNum] = pRegionGoalDimm->pDimm; SpecifiedDimmsOnSocketAsymRegionSize[SpecifiedDimmsOnSocketAsymNum] = pRegionGoalDimm->RegionSize; SpecifiedDimmsOnSocketAsymNum++; } } } if (SpecifiedDimmsOnSocketNum == 0) { continue; } ExistingRegionsNumOnSocket = 0; /** Create goal interleave sets and validate them **/ /** Interleaved **/ for (Index = 0, NewRegionsGoalNum = 0; Index < RegionGoalTemplatesNum; Index++) { if (RegionGoalTemplates[Index].InterleaveSetType == NON_INTERLEAVED) { continue; } RegionGoalSequenceIndex = Index; /** Need to split REGION into sockets. So we have to calculate a size of REGION on a given socket. **/ if (RegionGoalTemplates[Index].Asymmetrical) { if (SpecifiedDimmsOnSocketAsymNum == 0) { continue; } InterleaveSetSize = 0; for (Index2 = 0; Index2 < SpecifiedDimmsOnSocketAsymNum; Index2++) { InterleaveSetSize += SpecifiedDimmsOnSocketAsymRegionSize[Index2]; } ReturnCode = PerformInterleavingAndCreateGoal(&RegionGoalTemplates[Index], pSpecifiedDimmsOnSocketAsym, SpecifiedDimmsOnSocketAsymNum, InterleaveSetSize, pDriverPreferences, (UINT16)RegionGoalSequenceIndex, pNewRegionsGoal, &NewRegionsGoalNum, &AvailableISIndex); } else { InterleaveSetSize = 0; for (Index2 = 0; Index2 < SpecifiedDimmsOnSocketNum; Index2++) { InterleaveSetSize += SpecifiedDimmsOnSocketRegionSize[Index2]; } ReturnCode = PerformInterleavingAndCreateGoal(&RegionGoalTemplates[Index], pSpecifiedDimmsOnSocket, SpecifiedDimmsOnSocketNum, InterleaveSetSize, pDriverPreferences, (UINT16)RegionGoalSequenceIndex, pNewRegionsGoal, &NewRegionsGoalNum, &AvailableISIndex); } if (EFI_ERROR(ReturnCode)) { goto FinishSimpleClean; } } /** Non-interleaved **/ for (Index = 0; Index < RegionGoalTemplatesNum; Index++) { if (RegionGoalTemplates[Index].InterleaveSetType != NON_INTERLEAVED) { continue; } RegionGoalSequenceIndex = Index; if (RegionGoalTemplates[Index].Asymmetrical) { for (Index2 = 0; Index2 < SpecifiedDimmsOnSocketAsymNum; Index2++) { InterleaveSetSize = SpecifiedDimmsOnSocketAsymRegionSize[Index2]; /* Corner case, Ex: On some platforms with 5 Dimms per socket AD Interleaved mode with all asymmetric DIMMs (different capacities) Instead of having one AD interleaved region we can have more */ if (NewRegionsGoalNum == MaxPMInterleaveSets.MaxInterleaveSetsSplit.PerDie) { SetCmdStatus(pCommandStatus, NVM_WARN_REGION_AD_NI_PM_INTERLEAVE_SETS_REDUCED); break; } pNewRegionsGoal[NewRegionsGoalNum] = CreateRegionGoal(&RegionGoalTemplates[Index], &pSpecifiedDimmsOnSocketAsym[Index2], 1, InterleaveSetSize, pDriverPreferences, (UINT16)RegionGoalSequenceIndex, &AvailableISIndex); if (pNewRegionsGoal[NewRegionsGoalNum] == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto FinishSimpleClean; } NewRegionsGoalNum++; } } else { for (Index2 = 0; Index2 < SpecifiedDimmsOnSocketNum; Index2++) { InterleaveSetSize = SpecifiedDimmsOnSocketRegionSize[Index2]; /* For AD non-interleaved, only MaxPMInterleaveSetsPerDie DIMMs configured as AD X1, others are unmapped Goal cannot be created of size Zero Count the no of existing AD x1 regions on these DCPMMs, if any */ if (InterleaveSetSize == 0) { if (IsDimmManageable(pSpecifiedDimmsOnSocket[Index2])) { for (Index = 0; Index < pSpecifiedDimmsOnSocket[Index2]->ISsNum; Index++) { if (pSpecifiedDimmsOnSocket[Index2]->pISs[Index]->InterleaveFormatWays == INTERLEAVE_SET_1_WAY) { ExistingRegionsNumOnSocket++; } } } continue; } pNewRegionsGoal[NewRegionsGoalNum] = CreateRegionGoal(&RegionGoalTemplates[Index], &pSpecifiedDimmsOnSocket[Index2], 1, InterleaveSetSize, pDriverPreferences, (UINT16)RegionGoalSequenceIndex, &AvailableISIndex); if (pNewRegionsGoal[NewRegionsGoalNum] == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto FinishSimpleClean; } NewRegionsGoalNum++; } ReturnCode = IsConfigureWholeSocket(SpecifiedDimmsOnSocketNum, Socket, &WholeSocket); if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); NVDIMM_DBG("Unable to determine if goal request is configuring entire socket or adding unconfigured dimms"); goto Finish; } /* Corner case, Ex: On some platforms, with 6 AD X1s and 2 Dimms with unmapped capacities If we delete PCD on 2 Dimms with inaccessible memory and request goal creation with AD non-interleaved targeting these 2 Dimms, the total no of regions can exceed the MaxPMInterleaveSetsPerDie limit */ if (!WholeSocket) { LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (!IsDimmManageable(pDimm)) { continue; } if (pDimm->SocketId == Socket && (!IsPointerInArray((VOID **)pSpecifiedDimmsOnSocket, SpecifiedDimmsOnSocketNum, pDimm))) { for (Index = 0; Index < pDimm->ISsNum; Index++) { if (pDimm->pISs[Index]->InterleaveFormatWays == INTERLEAVE_SET_1_WAY) { ExistingRegionsNumOnSocket++; } } } } } if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision) && ((ExistingRegionsNumOnSocket + NewRegionsGoalNum) > MaxPMInterleaveSets.MaxInterleaveSetsSplit.PerDie)) { ResetCmdStatus(pCommandStatus, NVM_ERR_REGION_MAX_PM_INTERLEAVE_SETS_EXCEEDED); ReturnCode = EFI_ABORTED; goto Finish; } } } ReturnCode = VerifyInterleaveSetsPlatformSupport(pNewRegionsGoal, NewRegionsGoalNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto FinishSimpleClean; } /** If all goal interleave sets have been validated successfully, then we can assign them to DIMMs **/ for (Index = 0; Index < RelatedDimmsOnSocketNum; Index++) { pRelatedDimmsOnSocket[Index]->RegionsGoalNum = 0; } for (Index = 0; Index < NewRegionsGoalNum; Index++) { for (Index2 = 0; Index2 < pNewRegionsGoal[Index]->DimmsNum; Index2++) { pDimm = pNewRegionsGoal[Index]->pDimms[Index2]; RegionGoalSequenceIndex = pNewRegionsGoal[Index]->SequenceIndex; pDimm->pRegionsGoal[RegionGoalSequenceIndex] = pNewRegionsGoal[Index]; /** Determine a number of Regions Goal for Dimm**/ if (RegionGoalSequenceIndex + 1 > pDimm->RegionsGoalNum) { pDimm->RegionsGoalNum = RegionGoalSequenceIndex + 1; } } } for (Index = 0; Index < RelatedDimmsOnSocketNum; Index++) { pRegionGoalDimm = FindRegionGoalDimm(DimmsSym, DimmsSymNum, pRelatedDimmsOnSocket[Index]); if (pRegionGoalDimm != NULL) { pRelatedDimmsOnSocket[Index]->RegionsGoalConfig = TRUE; pRelatedDimmsOnSocket[Index]->VolatileSizeGoal = pRegionGoalDimm->VolatileSize; } else { pRelatedDimmsOnSocket[Index]->RegionsGoalConfig = FALSE; for (Index2 = 0; Index2 < MAX_IS_PER_DIMM; Index2++) { pRelatedDimmsOnSocket[Index]->pRegionsGoal[Index2] = NULL; } pRelatedDimmsOnSocket[Index]->RegionsGoalNum = 0; pRelatedDimmsOnSocket[Index]->VolatileSizeGoal = 0; } pRelatedDimmsOnSocket[Index]->GoalConfigStatus = GOAL_CONFIG_STATUS_NEW; pRelatedDimmsOnSocket[Index]->PcdSynced = FALSE; } } if (pReserveDimm != NULL) { if (ReserveDimmType == RESERVE_DIMM_AD_NOT_INTERLEAVED) { ResDimmRegionGoalTemplate.InterleaveSetType = NON_INTERLEAVED; ResDimmRegionGoalTemplate.Size = ROUNDDOWN(pReserveDimm->RawCapacity, gNvmDimmData->Alignments.RegionPersistentAlignment); pReserveDimmRegionsGoal = CreateRegionGoal(&ResDimmRegionGoalTemplate, &pReserveDimm, 1, ResDimmRegionGoalTemplate.Size, pDriverPreferences, 0, &AvailableISIndex); if (pReserveDimmRegionsGoal == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto FinishAdvanceClean; } ReturnCode = VerifyInterleaveSetsPlatformSupport(&pReserveDimmRegionsGoal, 1, pCommandStatus); if (EFI_ERROR(ReturnCode)) { FREE_POOL_SAFE(pReserveDimmRegionsGoal); goto FinishAdvanceClean; } pReserveDimm->RegionsGoalNum = 1; pReserveDimm->pRegionsGoal[0] = pReserveDimmRegionsGoal; } else { pReserveDimm->RegionsGoalNum = 0; } pReserveDimm->RegionsGoalConfig = TRUE; pReserveDimm->VolatileSizeGoal = 0; pReserveDimm->GoalConfigStatus = GOAL_CONFIG_STATUS_NEW; pReserveDimm->PcdSynced = FALSE; } for (Index = 0; Index < ExistingRegionsGoalNum; Index++) { FREE_POOL_SAFE(pExistingRegionsGoal[Index]); } goto Finish; FinishSimpleClean: for (Index = 0; Index < NewRegionsGoalNum; Index++) { FREE_POOL_SAFE(pNewRegionsGoal[Index]); } FinishAdvanceClean: ClearInternalGoalConfigsInfo(&gNvmDimmData->PMEMDev.Dimms); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Delete regions goal configs from input DIMMs and (if force is true) all DIMMs related by regions @param[in, out] pDimms Array of pointers to DIMMs @param[in] DimmsNum Number of pointers in pDimms @param[in] Force Force to perform deleting regions goal configs on all affected DIMMs @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL or input DIMMs are different than affected DIMMs **/ EFI_STATUS DeleteRegionsGoalConfigs( IN OUT DIMM *pDimms[], IN UINT32 DimmsNum, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS Rc = EFI_SUCCESS; DIMM *pRelatedDimms[MAX_DIMMS]; UINT32 RelatedDimmsNum = 0; REGION_GOAL *pExistingRegionsGoal[MAX_IS_CONFIGS]; UINT32 ExistingRegionsGoalNum = 0; UINT32 Index = 0; UINT32 Index2 = 0; NVDIMM_ENTRY(); if (pDimms == NULL || pCommandStatus == NULL) { Rc = EFI_INVALID_PARAMETER; goto Finish; } SetMem(pRelatedDimms, sizeof(pRelatedDimms), 0x0); SetMem(pExistingRegionsGoal, sizeof(pExistingRegionsGoal), 0x0); Rc = FindRelatedDimmsByRegionGoalConfigs(pDimms, DimmsNum, pRelatedDimms, &RelatedDimmsNum); if (EFI_ERROR(Rc)) { goto Finish; } Rc = FindUniqueRegionsGoal(pRelatedDimms, RelatedDimmsNum, pExistingRegionsGoal, &ExistingRegionsGoalNum); if (EFI_ERROR(Rc)) { goto Finish; } /** The found list of DIMMs doesn't have the same elements as the input list **/ if (DimmsNum != RelatedDimmsNum) { for (Index = 0; Index < RelatedDimmsNum; Index++) { if (!IsPointerInArray((VOID **) pDimms, DimmsNum, pRelatedDimms[Index])) { SetObjStatusForDimm(pCommandStatus, pRelatedDimms[Index], NVM_ERR_REGION_GOAL_CONF_AFFECTS_UNSPEC_DIMM); } } Rc = EFI_INVALID_PARAMETER; SetCmdStatus(pCommandStatus, NVM_ERR_REGION_GOAL_CONF_AFFECTS_UNSPEC_DIMM); goto Finish; } for (Index = 0; Index < RelatedDimmsNum; Index++) { if (pRelatedDimms[Index]->RegionsGoalConfig) { pRelatedDimms[Index]->RegionsGoalConfig = FALSE; for (Index2 = 0; Index2 < MAX_IS_PER_DIMM; Index2++) { pRelatedDimms[Index]->pRegionsGoal[Index2] = NULL; } pRelatedDimms[Index]->RegionsGoalNum = 0; pRelatedDimms[Index]->VolatileSizeGoal = 0; pRelatedDimms[Index]->GoalConfigStatus = GOAL_CONFIG_STATUS_NO_GOAL_OR_SUCCESS; pRelatedDimms[Index]->PcdSynced = FALSE; } else { pRelatedDimms[Index]->PcdSynced = TRUE; SetObjStatusForDimm(pCommandStatus, pRelatedDimms[Index], NVM_ERR_REGION_NO_GOAL_EXISTS_ON_DIMM); } } ClearRegionsGoal(pRelatedDimms, RelatedDimmsNum, pExistingRegionsGoal, ExistingRegionsGoalNum); Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** Check if specified persistent memory type contain valid value @param[in] PersistentMemType Persistent memory type @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Specified value is invalid **/ EFI_STATUS PersistentMemoryTypeValidation( IN UINT8 PersistentMemType ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); switch (PersistentMemType) { case PM_TYPE_AD: case PM_TYPE_AD_NI: case PM_TYPE_RESERVED: ReturnCode = EFI_SUCCESS; break; default: goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Select one reserve Dimm from specified list of Dimms and remove it from the list @param[in, out] pDimms Array of pointers to DIMMs @param[in, out] pDimmsNum Number of pointers in pDimms @param[out] ppReserveDimm Selected Dimm from the list @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All Ok **/ EFI_STATUS SelectReserveDimm( IN OUT DIMM *pDimms[MAX_DIMMS], IN OUT UINT32 *pDimmsNum, OUT DIMM **ppReserveDimm ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; UINT32 SelectedIndex = 0; BOOLEAN Found = FALSE; NVDIMM_ENTRY(); if (pDimms == NULL || pDimmsNum == NULL || ppReserveDimm == NULL || *pDimmsNum < 1) { goto Finish; } /** Get the last manageable one from array as reserve Dimm **/ SelectedIndex = *pDimmsNum; while (!Found && (SelectedIndex > 0)) { SelectedIndex--; *ppReserveDimm = pDimms[SelectedIndex]; Found = TRUE; /** Remove reserve Dimm from array **/ (*pDimmsNum)--; for (Index = SelectedIndex; Index < *pDimmsNum; Index++) { pDimms[Index] = pDimms[Index + 1]; } ReturnCode = EFI_SUCCESS; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Calculate system wide capacity for a given percent @param[in] pDimms Array of pointers to DIMMs @param[in] pDimmsNum Number of pointers in pDimms @param[in] Percent Percent to calculate @param[out] pDimmsCapacity Output dimms capacity in bytes @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS Success **/ EFI_STATUS CalculateDimmCapacityFromPercent( IN DIMM *pDimms[MAX_DIMMS], IN UINT32 DimmsNum, IN UINT32 Percent, OUT UINT64 *pDimmsCapacity ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; UINT64 DimmsCapacity = 0; NVDIMM_ENTRY(); if (pDimms == NULL || pDimmsCapacity == NULL) { goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { DimmsCapacity += pDimms[Index]->RawCapacity; } *pDimmsCapacity = DimmsCapacity * Percent / 100; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Verify that all specified features in goal config are supported by platform @param[in] VolatileSize Volatile region size @param[in] PersistentMemType Persistent memory type @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER If one or more parameters are NULL @retval EFI_UNSUPPORTED A given config is unsupported @retval EFI_LOAD_ERROR There is no PCAT **/ EFI_STATUS VerifyPlatformSupport( IN UINT64 VolatileSize, IN UINT8 PersistentMemType, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; BOOLEAN AppDirect = FALSE; BOOLEAN ConfigChangeSupported = FALSE; MEMORY_MODE_CAPABILITIES MemModeCapabilities; NVDIMM_ENTRY(); if (pCommandStatus == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ZeroMem(&MemModeCapabilities, sizeof(MEMORY_MODE_CAPABILITIES)); /** Check if BIOS supports changing configuration through management software **/ CHECK_RESULT(CheckIfBiosSupportsConfigChange(&ConfigChangeSupported), Finish); if (!ConfigChangeSupported) { ResetCmdStatus(pCommandStatus, NVM_ERR_PLATFORM_NOT_SUPPORT_MANAGEMENT_SOFT); ReturnCode = EFI_UNSUPPORTED; goto Finish; } CHECK_RESULT(CheckMemModeCapabilities(&MemModeCapabilities), Finish); /** Check if the platform supports 2LM Mode **/ if (VolatileSize > 0 && !MemModeCapabilities.MemoryModesFlags.Memory) { ResetCmdStatus(pCommandStatus, NVM_ERR_PLATFORM_NOT_SUPPORT_2LM_MODE); ReturnCode = EFI_UNSUPPORTED; goto Finish; } /** Check if the platform supports PM-Direct Mode **/ AppDirect = PersistentMemType == PM_TYPE_AD || PersistentMemType == PM_TYPE_AD_NI; if (AppDirect && !MemModeCapabilities.MemoryModesFlags.AppDirect) { ResetCmdStatus(pCommandStatus, NVM_ERR_PLATFORM_NOT_SUPPORT_PM_MODE); ReturnCode = EFI_UNSUPPORTED; goto Finish; } /** Check if the platform supports Mixed Mode **/ if (VolatileSize > 0 && AppDirect && !MemModeCapabilities.MemoryModesFlags.MixedMode) { ResetCmdStatus(pCommandStatus, NVM_ERR_PLATFORM_NOT_SUPPORT_MIXED_MODE); ReturnCode = EFI_UNSUPPORTED; goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check if specified AppDirect Settings contain valid values @param[in] pDriverPreferences Driver preferences for AppDirect Provisioning @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER parameter is NULL or specified values are not valid @retval EFI_ABORTED Unable to find required system tables **/ EFI_STATUS AppDirectSettingsValidation( IN DRIVER_PREFERENCES *pDriverPreferences ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; BOOLEAN Found = FALSE; UINT32 *pChannelInterleaveSize = NULL; UINT32 *piMCInterleaveSize = NULL; UINT32 InterleaveSizesSupportedLength = 0; NVDIMM_ENTRY(); if (pDriverPreferences == NULL) { goto Finish; } if (pDriverPreferences->AppDirectGranularity > APPDIRECT_GRANULARITY_MAX) { goto Finish; } /** XOR - both properties has to be set as default or both unset **/ if ((pDriverPreferences->ImcInterleaving == DEFAULT_IMC_INTERLEAVE_SIZE) != (pDriverPreferences->ChannelInterleaving == DEFAULT_CHANNEL_INTERLEAVE_SIZE)) { goto Finish; } ReturnCode = RetrieveSupportediMcAndChannelInterleaveSizes(&pChannelInterleaveSize, &piMCInterleaveSize, NULL, NULL, &InterleaveSizesSupportedLength, NULL, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to retrieve supported iMc & Channel Interleave sizes from PCAT"); goto Finish; } if (pDriverPreferences->ImcInterleaving != DEFAULT_IMC_INTERLEAVE_SIZE) { Found = FALSE; for (Index = 0; Index < InterleaveSizesSupportedLength; Index++) { if (pDriverPreferences->ImcInterleaving & piMCInterleaveSize[Index]) { Found = TRUE; break; } } if (!Found) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } if (pDriverPreferences->ChannelInterleaving != DEFAULT_CHANNEL_INTERLEAVE_SIZE) { Found = FALSE; for (Index = 0; Index < InterleaveSizesSupportedLength; Index++) { if (pDriverPreferences->ChannelInterleaving & pChannelInterleaveSize[Index]) { Found = TRUE; break; } } if (!Found) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); FREE_POOL_SAFE(pChannelInterleaveSize); FREE_POOL_SAFE(piMCInterleaveSize); return ReturnCode; } /** Check if specified AppDirect Settings will conflict with existing AppDirect Interleaves @param[in] pDriverPreferences Driver preferences for AppDirect Provisioning @param[out] pConflict True, conflict exists with existing AppDirect Memory @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER parameter is NULL **/ EFI_STATUS AppDirectSettingsConflict( IN DRIVER_PREFERENCES *pDriverPreferences, OUT BOOLEAN *pConflict, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; struct _NVM_IS *pNvm_IS = NULL; UINT32 Index = 0; UINT32 Index2 = 0; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; UINT8 RequestedImcInterleaving = 0; UINT8 RequestedChannelInterleaving = 0; UINT32 *pChannelInterleaveSize = NULL; UINT32 *piMCInterleaveSize = NULL; UINT32 *pRecommendedFormats = NULL; UINT32 InterleaveSizesSupportedLength = 0; NVDIMM_ENTRY(); *pConflict = FALSE; if (pCommandStatus == NULL || pDriverPreferences == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = RetrieveSupportediMcAndChannelInterleaveSizes(&pChannelInterleaveSize, &piMCInterleaveSize, &pRecommendedFormats, NULL, &InterleaveSizesSupportedLength, NULL, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to retrieve supported iMc & Channel Interleave sizes from PCAT"); goto Finish; } /** If default we have to retrieve the default settings from PCAT **/ if (pDriverPreferences->ImcInterleaving == DEFAULT_IMC_INTERLEAVE_SIZE && pDriverPreferences->ChannelInterleaving == DEFAULT_CHANNEL_INTERLEAVE_SIZE) { if (pRecommendedFormats != NULL) { for (Index2 = 0; Index2 < InterleaveSizesSupportedLength; Index2++) { if (IS_BIT_SET_VAR(pRecommendedFormats[Index2], BIT0)) { RequestedImcInterleaving = (UINT8)piMCInterleaveSize[Index2]; RequestedChannelInterleaving = (UINT8)pChannelInterleaveSize[Index2]; break; } } } else { RequestedImcInterleaving = (UINT8)piMCInterleaveSize[Index2]; RequestedChannelInterleaving = (UINT8)pChannelInterleaveSize[Index2]; } } else { RequestedChannelInterleaving = pDriverPreferences->ChannelInterleaving; RequestedImcInterleaving = pDriverPreferences->ImcInterleaving; } LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (!IsDimmManageable(pDimm)) { continue; } if (pDimm->Configured) { for (Index = 0; Index < pDimm->ISsNum; Index++) { pNvm_IS = pDimm->pISs[Index]; if (pNvm_IS->InterleaveFormatWays > 0 && (pNvm_IS->InterleaveFormatImc != RequestedImcInterleaving || pNvm_IS->InterleaveFormatChannel != RequestedChannelInterleaving)) { *pConflict = TRUE; break; } } } if (*pConflict) { ResetCmdStatus(pCommandStatus, NVM_ERR_APPDIRECT_IN_SYSTEM); break; } } Finish: NVDIMM_EXIT_I64(ReturnCode); FREE_POOL_SAFE(pChannelInterleaveSize); FREE_POOL_SAFE(piMCInterleaveSize); FREE_POOL_SAFE(pRecommendedFormats); return ReturnCode; } /** For a given set of region goal dimms reduce the volatile capacity of the region based on the requested reserved size. It is assumed that no region capacity exists on the region goals. @param[in out] pReservedSize Size to reduce the region capacity @param[in out] RegionGoalDimms Array of region goal dimms to reduce @param[in out] pRegionGoalDimmsNum number of elements in the array @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_ABORTED persistent capacity discovered on one or more region goals. **/ STATIC EFI_STATUS ReduceVolatileCapacityPerReservedCapacity( IN OUT UINT64 *pReservedSize, IN OUT REGION_GOAL_DIMM RegionGoalDimms[MAX_DIMMS], IN OUT UINT32 *pRegionGoalDimmsNum ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT64 TotalRegionGoalCapacity = 0; UINT32 Index = 0; UINT32 Index2 = 0; UINT64 ReduceBy = 0; UINT64 CurrentLargestDimm = 0; UINT64 SecondLargestDimm = 0; UINT32 NumOfLargestDimms = 0; UINT64 MaxReducePerDIMM = 0; NVDIMM_ENTRY(); if (pReservedSize == NULL || RegionGoalDimms == NULL || pRegionGoalDimmsNum == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (*pReservedSize == 0) { goto Finish; } // If capacity in the dimms is less than the amount requested // then take all capacity in dimms for (Index = 0; Index < *pRegionGoalDimmsNum; Index++) { TotalRegionGoalCapacity += RegionGoalDimms[Index].VolatileSize; if (RegionGoalDimms[Index].RegionSize > 0) { NVDIMM_DBG("Cannot reduce volatile capacity while region capacity exists"); ReturnCode = EFI_ABORTED; } } if (TotalRegionGoalCapacity <= *pReservedSize) { for (Index = 0; Index < *pRegionGoalDimmsNum; Index++) { *pReservedSize -= RegionGoalDimms[Index].VolatileSize; } *pRegionGoalDimmsNum = 0; } else { // When reducing capacity we don't want to stop unless we have consumed all the goals or // we have reduced the requested amount. Reduce by consuming the asymmetrical size segments first. for (Index2 = 0; Index2 < *pRegionGoalDimmsNum; Index2++) { CurrentLargestDimm = 0; SecondLargestDimm = 0; NumOfLargestDimms = 0; MaxReducePerDIMM = 0; for (Index = 0; Index < *pRegionGoalDimmsNum; Index++) { CurrentLargestDimm = (RegionGoalDimms[Index].VolatileSize > CurrentLargestDimm) ? RegionGoalDimms[Index].VolatileSize : CurrentLargestDimm; } for (Index = 0; Index < *pRegionGoalDimmsNum; Index++) { SecondLargestDimm = (RegionGoalDimms[Index].VolatileSize != CurrentLargestDimm && RegionGoalDimms[Index].VolatileSize > SecondLargestDimm) ? RegionGoalDimms[Index].VolatileSize : SecondLargestDimm; } MaxReducePerDIMM = CurrentLargestDimm - SecondLargestDimm; for (Index = 0; Index < *pRegionGoalDimmsNum; Index++) { if (RegionGoalDimms[Index].VolatileSize == CurrentLargestDimm) { NumOfLargestDimms++; } } if (NumOfLargestDimms == 0) { ReturnCode = EFI_ABORTED; goto Finish; } // If reserved capacity does not consume the dimms then try to reduce each dimm evenly in allocations // of aligned volatile capacity. volatile size should already be aligned to Volatile Alignment ReduceBy = *pReservedSize / NumOfLargestDimms; ReduceBy = ROUNDUP(ReduceBy, gNvmDimmData->Alignments.RegionVolatileAlignment); ReduceBy = ReduceBy > MaxReducePerDIMM ? MaxReducePerDIMM : ReduceBy; for (Index = 0; Index < *pRegionGoalDimmsNum; Index++) { if (RegionGoalDimms[Index].VolatileSize != CurrentLargestDimm) { continue; } // reduce each DIMM evenly if possible if (RegionGoalDimms[Index].VolatileSize >= ReduceBy && *pReservedSize >= ReduceBy) { RegionGoalDimms[Index].VolatileSize -= ReduceBy; *pReservedSize -= ReduceBy; } else if (RegionGoalDimms[Index].VolatileSize >= ReduceBy && *pReservedSize < ReduceBy) { RegionGoalDimms[Index].VolatileSize -= ReduceBy; *pReservedSize = 0; } else { *pReservedSize -= RegionGoalDimms[Index].VolatileSize; RegionGoalDimms[Index].VolatileSize = 0; } } // Recalculate Total remaining capacity for next loop for (Index = 0; Index < *pRegionGoalDimmsNum; Index++) { TotalRegionGoalCapacity += RegionGoalDimms[Index].VolatileSize; } if (TotalRegionGoalCapacity == 0 || *pReservedSize == 0) { break; } } if (TotalRegionGoalCapacity > 0 && *pReservedSize > 0) { ReturnCode = EFI_DEVICE_ERROR; NVDIMM_DBG("Unable to correctly map reserved capacity"); } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Reduce system wide socket mapped memory to align with the system memory mapped SKU limits per Socket. @param[in] Socket Socket Id for SKU limit calculations @param[in] pDimms Array of pointers to manageable DIMMs only @param[in] NumDimmsOnSocket Number of pointers in pDimms @param[in out] DimmsSymmetricalOnSocket Array of Dimms for symmetrical region config @param[in out] pDimmsSymmetricalNumOnSocket Returned number of items in DimmsSymmetrical @param[in out] DimmsAsymmetricalOnSocket Array of Dimms for asymmetrical region config @param[in out] pDimmsAsymmetricalNumOnSocket Returned number of items in DimmsAsymmetrical @param[in out] RegionGoalTemplates Array of region goal templates @param[in out] pRegionGoalTemplatesNum Number of items in RegionGoalTemplates @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_UNSUPPORTED Bad values retrieved from PCAT **/ EFI_STATUS ReduceCapacityForSocketSKU( IN UINT32 Socket, IN DIMM *pDimmsOnSocket[MAX_DIMMS], IN UINT32 NumDimmsOnSocket, IN OUT REGION_GOAL_DIMM DimmsSymmetricalOnSocket[MAX_DIMMS], IN OUT UINT32 *pDimmsSymmetricalNumOnSocket, IN OUT REGION_GOAL_DIMM DimmsAsymmetricalOnSocket[MAX_DIMMS], IN OUT UINT32 *pDimmsAsymmetricalNumOnSocket, IN OUT REGION_GOAL_TEMPLATE RegionGoalTemplates[MAX_IS_PER_DIMM], IN OUT UINT32 *pRegionGoalTemplatesNum, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; BOOLEAN WholeSocket = FALSE; UINT64 TotalRequestedMemoryOnSocket = 0; BOOLEAN NewConfigurationMemoryMode = FALSE; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; UINT32 Index = 0; UINT64 ReduceCapacity = 0; UINT64 MappedMemorySizeLimit = 0; UINT64 DDRRawCapacity = 0; UINT64 DDRCacheUsableCapacity = 0; MEMORY_MODE AllowedMode = MEMORY_MODE_1LM; NVDIMM_ENTRY(); if (pDimmsOnSocket == NULL || NumDimmsOnSocket == 0 || DimmsSymmetricalOnSocket == NULL || pDimmsSymmetricalNumOnSocket == NULL || DimmsAsymmetricalOnSocket == NULL || pDimmsAsymmetricalNumOnSocket == NULL || RegionGoalTemplates == NULL || pRegionGoalTemplatesNum == NULL || pCommandStatus == NULL) { goto Finish; } TotalRequestedMemoryOnSocket = 0; // MemoryMode only exists on symmetrical dimms objects for (Index = 0; Index < *pDimmsSymmetricalNumOnSocket; Index++) { if (DimmsSymmetricalOnSocket[Index].VolatileSize > 0) { NewConfigurationMemoryMode = TRUE; break; } } // If we are adding just a single dimm then the system memory mode // can be defined by pre-existing dimms if (!NewConfigurationMemoryMode && !WholeSocket) { LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (Socket == pDimm->SocketId && IsDimmManageable(pDimm) && pDimm->MappedVolatileCapacity > 0 && pDimm->Configured) { NewConfigurationMemoryMode = TRUE; break; } } } ReturnCode = GetDDRCapacities((UINT16)Socket, &DDRRawCapacity, NULL, NULL, NULL); if (EFI_ERROR(ReturnCode)) { // If not Purley, this is an error. On Purley we expect the PMTT table // to be missing on some platforms, workarounds are further below if (!IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr)) { // Preserve return code and error out NVDIMM_DBG("Could not retrieve DDR raw capacity"); goto Finish; } if (!NewConfigurationMemoryMode) { // None of the DDR is used as cache, it's fully volatile, so we need to // know the value to correctly calculate if we're violating the sku // limit. However, since we're on Purley and are probably missing the // PMTT table, for backwards compatibility we want to skip this ReduceCapacityForSocketSku // check and show a warning. NVDIMM_ERR("Getting out early with warning"); ReturnCode = EFI_SUCCESS; SetCmdStatus(pCommandStatus, NVM_WARN_PMTT_TABLE_NOT_FOUND); goto Finish; } // All of the DDR is used as cache, so we don't need to know the current // DDR raw capacity, continue on without a warning DDRRawCapacity = 0; ReturnCode = EFI_SUCCESS; } ReturnCode = AllowedMemoryMode(&AllowedMode); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to determine allowed memory mode."); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } ReturnCode = IsConfigureWholeSocket(NumDimmsOnSocket, Socket, &WholeSocket); if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); NVDIMM_DBG("Unable to determine if goal request is configuring entire socket or adding unconfigured dimms"); goto Finish; } ReturnCode = RetrievePcatSocketSkuMappedMemoryLimit(Socket, &MappedMemorySizeLimit); // If no PCAT tables exist for a socket then that socket will not be reduced. if (ReturnCode == EFI_NOT_FOUND) { ReturnCode = EFI_SUCCESS; goto Finish; } else if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to retrieve socket sku info table for socket"); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } for (Index = 0; Index < *pDimmsSymmetricalNumOnSocket; Index++) { TotalRequestedMemoryOnSocket += DimmsSymmetricalOnSocket[Index].RegionSize; TotalRequestedMemoryOnSocket += ROUNDDOWN( DimmsSymmetricalOnSocket[Index].VolatileSize, gNvmDimmData->Alignments.RegionVolatileAlignment); } for (Index = 0; Index < *pDimmsAsymmetricalNumOnSocket; Index++) { TotalRequestedMemoryOnSocket += DimmsAsymmetricalOnSocket[Index].RegionSize; } // Adding DDR capacity only if new config will contain 1LM if (!NewConfigurationMemoryMode) { TotalRequestedMemoryOnSocket += DDRRawCapacity; } else if (MEMORY_MODE_1LM_PLUS_2LM == AllowedMode) { // Get total DDR Cache Size ReturnCode = GetTotalUsableDDRCacheSize((UINT16)Socket, AllowedMode, &DDRCacheUsableCapacity); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Could not determine usable DDR Cache capacity."); goto Finish; } TotalRequestedMemoryOnSocket += DDRRawCapacity - DDRCacheUsableCapacity; } /** If adding a new dimm to a configured socket and the new configuration will be 1LM, then the total amount to be mapped will be old AD + new AD. **/ if (!NewConfigurationMemoryMode && !WholeSocket) { LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (Socket == pDimm->SocketId && !IsPointerInArray((VOID **)pDimmsOnSocket, NumDimmsOnSocket, pDimm) && IsDimmManageable(pDimm)) { TotalRequestedMemoryOnSocket += pDimm->MappedPersistentCapacity; } } /** If adding a new dimm to a configured socket and the new configuration will be MemoryMode, then the total amount to be mapped will be All of the old AD + new AD + new MemoryMode + old MemoryMode. **/ } else if (NewConfigurationMemoryMode && !WholeSocket) { LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (Socket == pDimm->SocketId && !IsPointerInArray((VOID **)pDimmsOnSocket, NumDimmsOnSocket, pDimm) && IsDimmManageable(pDimm)) { TotalRequestedMemoryOnSocket += pDimm->MappedPersistentCapacity; TotalRequestedMemoryOnSocket += pDimm->MappedVolatileCapacity; } } } // Reduce capacity on socket if larger than the amount we can map. if (TotalRequestedMemoryOnSocket > MappedMemorySizeLimit) { SetObjStatus(pCommandStatus, Socket, NULL, 0, NVM_WARN_MAPPED_MEM_REDUCED_DUE_TO_CPU_SKU, ObjectTypeSocket); ReduceCapacity = TotalRequestedMemoryOnSocket - MappedMemorySizeLimit; // Reduce AppDirect first and then Volatile ReturnCode = ReduceAppDirectCapacityPerReservedCapacity (&ReduceCapacity, DimmsAsymmetricalOnSocket, pDimmsAsymmetricalNumOnSocket); if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } ReturnCode = ReduceAppDirectCapacityPerReservedCapacity (&ReduceCapacity, DimmsSymmetricalOnSocket, pDimmsSymmetricalNumOnSocket); if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } ReturnCode = ReduceVolatileCapacityPerReservedCapacity (&ReduceCapacity, DimmsSymmetricalOnSocket, pDimmsSymmetricalNumOnSocket); if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } // If we consumed all AD and no volatile exists configure for PM_STORAGE if (*pDimmsSymmetricalNumOnSocket == 0) { for (Index = 0; Index < NumDimmsOnSocket; Index++) { DimmsSymmetricalOnSocket[Index].pDimm = pDimmsOnSocket[Index]; DimmsSymmetricalOnSocket[Index].RegionSize = 0; DimmsSymmetricalOnSocket[Index].VolatileSize = 0; (*pDimmsSymmetricalNumOnSocket)++; } } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Verify DIMMs SKU support @param[in] ppDimms Array of dimms @param[in] DimmsNum Number of dimms @param[in, out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS No SKU violation @retval EFI_ABORTED SKU violation @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS VerifySKUSupportForCreateGoal( IN DIMM **ppDimms, IN UINT32 DimmsNum, IN OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; DIMM *pDimm = NULL; BOOLEAN VolatileSkuViolated = FALSE; BOOLEAN AppDirectSkuViolated = FALSE; NVDIMM_ENTRY(); if (ppDimms == NULL || pCommandStatus == NULL) { goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { pDimm = ppDimms[Index]; VolatileSkuViolated = pDimm->VolatileSizeGoal > 0 && pDimm->SkuInformation.MemoryModeEnabled == MODE_DISABLED; AppDirectSkuViolated = pDimm->RegionsGoalNum > 0 && pDimm->SkuInformation.AppDirectModeEnabled == MODE_DISABLED; if (VolatileSkuViolated || AppDirectSkuViolated) { SetObjStatusForDimm(pCommandStatus, pDimm, NVM_ERR_CONFIG_NOT_SUPPORTED_BY_CURRENT_SKU); ReturnCode = EFI_ABORTED; goto Finish; } } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Clear previous regions goal configs and - if regions goal configs is specified - replace them with new one. 1. Clear previous regions goal configs on all affected dimms 2. [OPTIONAL] Send new regions goal configs to dimms 3. Set information about synchronization with dimms @param[in] pDimmList Head of the list of all NVM DIMMs in the system @param[in] DimmsNum Number of dimms in pDimmList @param[in] ReservedSizeIsZero Indicate whether the reserved size is zero @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pDimmList is NULL @retval return codes from SendConfigInputToDimm **/ EFI_STATUS ApplyGoalConfigsToDimms( IN DIMM **ppDimms, IN UINT32 DimmsNum, IN BOOLEAN ReservedSizeIsZero, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; NVDIMM_PLATFORM_CONFIG_INPUT *pNewConfigInput = NULL; UINT32 Index = 0; NVDIMM_ENTRY(); CHECK_NULL_ARG(ppDimms, Finish); /** Clear previous regions goal configs **/ for (Index = 0; Index < DimmsNum; Index++) { pDimm = ppDimms[Index]; if (!IsDimmManageable(pDimm)) { continue; } if (pDimm->PcdSynced) { continue; } /** Remove Configuration Input table from Platform Config Data **/ ReturnCode = SendConfigInputToDimm(pDimm, NULL); if (EFI_ERROR(ReturnCode)) { SetObjStatusForDimm(pCommandStatus, pDimm, NVM_ERR_REGION_CONF_APPLYING_FAILED); goto Finish; } } /** Send new regions goal configs to dimms **/ for (Index = 0; Index < DimmsNum; Index++) { pDimm = ppDimms[Index]; if (!IsDimmManageable(pDimm) || !pDimm->RegionsGoalConfig) { continue; } if (pDimm->PcdSynced) { continue; } ReturnCode = GeneratePcdConfInput(pDimm, ReservedSizeIsZero, &pNewConfigInput); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Generating Platform Config Data Configuration Input failed."); SetObjStatusForDimm(pCommandStatus, pDimm, NVM_ERR_REGION_CONF_APPLYING_FAILED); goto Finish; } ReturnCode = SendConfigInputToDimm(pDimm, pNewConfigInput); if (EFI_ERROR(ReturnCode)) { SetObjStatusForDimm(pCommandStatus, pDimm, NVM_ERR_REGION_CONF_APPLYING_FAILED); goto Finish; } FREE_POOL_SAFE(pNewConfigInput); } /** If all data has been sent to dimms successfully, then we are synchronized **/ for (Index = 0; Index < DimmsNum; Index++) { pDimm = ppDimms[Index]; if (!IsDimmManageable(pDimm)) { continue; } if (pDimm->PcdSynced) { continue; } SetObjStatusForDimm(pCommandStatus, pDimm, NVM_SUCCESS); pDimm->PcdSynced = TRUE; } SetCmdStatus(pCommandStatus, NVM_SUCCESS); Finish: if (EFI_ERROR(ReturnCode) && (EFI_INVALID_PARAMETER != ReturnCode)) { // Create Goal ERROR! Try to remove Configuration Input table from Platform Config Data for (Index = 0; Index < DimmsNum; Index++) { pDimm = ppDimms[Index]; if (!IsDimmManageable(pDimm)) { continue; } if (pDimm->PcdSynced) { continue; } SendConfigInputToDimm(pDimm, NULL); } } FREE_POOL_SAFE(pNewConfigInput); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Send new Configuration Input to dimm. Get Platform Config Data from dimm, replace Configuration Input with new one, and send it back to dimm. If pNewConfigInput is NULL, then the function will send Platform Config Data without Configuration Input (old one will be removed). @param[in] pDimm dimm that we replace Platform Config Data for @param[in] pNewConfigInput new config input for dimm @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pDimm is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval other error codes from called functions: GetPlatformConfigDataOemPartition, SetPlatformConfigDataOemPartition **/ EFI_STATUS SendConfigInputToDimm( IN DIMM *pDimm, IN NVDIMM_PLATFORM_CONFIG_INPUT *pNewConfigInput OPTIONAL ) { EFI_STATUS Rc = EFI_SUCCESS; NVDIMM_CONFIGURATION_HEADER *pConfHeader = NULL; NVDIMM_CONFIGURATION_HEADER *pNewConfHeader = NULL; UINT32 PcdLength = 0; UINT32 CurrentOffset = 0; NVDIMM_ENTRY(); if (pDimm == NULL) { Rc = EFI_INVALID_PARAMETER; goto Finish; } /** Get current Platform Config Data from dimm **/ Rc = GetPlatformConfigDataOemPartition(pDimm, TRUE, &pConfHeader); #ifdef MEMORY_CORRUPTION_WA if (Rc == EFI_DEVICE_ERROR) { Rc = GetPlatformConfigDataOemPartition(pDimm, TRUE, &pConfHeader); } #endif // MEMORY_CORRUPTIO_WA if (EFI_ERROR(Rc)) { goto Finish; } /** Create new Platform Config Data **/ PcdLength = sizeof(NVDIMM_CONFIGURATION_HEADER) + pConfHeader->CurrentConfDataSize + (pNewConfigInput != NULL ? pNewConfigInput->Header.Length : 0) + pConfHeader->ConfOutputDataSize; pNewConfHeader = AllocateZeroPool(PcdLength); if (pNewConfHeader == NULL) { Rc = EFI_OUT_OF_RESOURCES; goto Finish; } /** Copy Configuration Header table **/ CopyMem_S(pNewConfHeader, PcdLength, pConfHeader, sizeof(*pConfHeader)); CurrentOffset += sizeof(*pConfHeader); /** Copy Current Config table **/ if (pConfHeader->CurrentConfStartOffset != 0 && pConfHeader->CurrentConfDataSize != 0) { CopyMem_S( (UINT8 *) pNewConfHeader + CurrentOffset, PcdLength - CurrentOffset, (UINT8 *) pConfHeader + pConfHeader->CurrentConfStartOffset, pConfHeader->CurrentConfDataSize); pNewConfHeader->CurrentConfStartOffset = CurrentOffset; pNewConfHeader->CurrentConfDataSize = pConfHeader->CurrentConfDataSize; CurrentOffset += pNewConfHeader->CurrentConfDataSize; } else { pNewConfHeader->CurrentConfStartOffset = 0; pNewConfHeader->CurrentConfDataSize = 0; } /** Copy new Configuration Input table **/ if (pNewConfigInput != NULL) { CopyMem_S( (UINT8 *) pNewConfHeader + CurrentOffset, PcdLength - CurrentOffset, pNewConfigInput, pNewConfigInput->Header.Length); pNewConfHeader->ConfInputStartOffset = CurrentOffset; pNewConfHeader->ConfInputDataSize = pNewConfigInput->Header.Length; CurrentOffset += pNewConfHeader->ConfInputDataSize; /** Update Configuration Header Revision Not needed for Purley platforms, as only one revision (0x1) is supported. **/ if (!IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pNewConfHeader)) { CopyMem_S(&pNewConfHeader->Header.Revision, sizeof(ACPI_REVISION), &pNewConfigInput->Header.Revision, sizeof(ACPI_REVISION)); } } else { pNewConfHeader->ConfInputStartOffset = 0; pNewConfHeader->ConfInputDataSize = 0; } /** Make COUT zero while applying a new CIN **/ pNewConfHeader->ConfOutputStartOffset = 0; pNewConfHeader->ConfOutputDataSize = 0; GenerateChecksum(pNewConfHeader, pNewConfHeader->Header.Length, PCAT_TABLE_HEADER_CHECKSUM_OFFSET); /** Send new Platform Config Data back to dimm **/ Rc = SetPlatformConfigDataOemPartition(pDimm, pNewConfHeader, PcdLength); if (EFI_ERROR(Rc)) { NVDIMM_DBG("Failed to set Platform Config Data"); goto Finish; } Finish: if (pNewConfHeader != NULL) { FreePool(pNewConfHeader); } if (pConfHeader != NULL) { FreePool(pConfHeader); } NVDIMM_EXIT_I64(Rc); return Rc; } /** Verify that specified Dimms don't affect other Dimms by current Regions or Regions goal configs @param[in, out] pDimms Array of pointers to DIMMs @param[in] DimmsNum Number of pointers in pDimms @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL or specified Dimms affect other Dimms **/ EFI_STATUS ValidateRegionsCorrelations( IN OUT DIMM *pDimms[], IN UINT32 DimmsNum, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS Rc = EFI_INVALID_PARAMETER; UINT32 Index = 0; DIMM *pRelatedDimms[MAX_DIMMS]; UINT32 RelatedDimmsNum = 0; OBJECT_STATUS *pObjectStatus = NULL; NvmStatusCode LastNvmStatus = NVM_LAST_STATUS_VALUE; NVDIMM_ENTRY(); SetMem(pRelatedDimms, sizeof(pRelatedDimms), 0x0); if (pDimms == NULL || pCommandStatus == NULL) { goto Finish; } Rc = FindRelatedDimmsByRegions(pDimms, DimmsNum, pRelatedDimms, &RelatedDimmsNum); if (EFI_ERROR(Rc)) { goto Finish; } if (DimmsNum != RelatedDimmsNum) { for (Index = 0; Index < RelatedDimmsNum; Index++) { if (!IsPointerInArray((VOID **) pDimms, DimmsNum, pRelatedDimms[Index])) { SetObjStatusForDimm(pCommandStatus, pRelatedDimms[Index], NVM_ERR_REGION_CURR_CONF_AFFECTS_UNSPEC_DIMM); } } LastNvmStatus = NVM_ERR_REGION_CURR_CONF_AFFECTS_UNSPEC_DIMM; } Rc = FindRelatedDimmsByRegionGoalConfigs(pDimms, DimmsNum, pRelatedDimms, &RelatedDimmsNum); if (EFI_ERROR(Rc)) { goto Finish; } if (DimmsNum != RelatedDimmsNum) { for (Index = 0; Index < RelatedDimmsNum; Index++) { if (!IsPointerInArray((VOID **) pDimms, DimmsNum, pRelatedDimms[Index])) { pObjectStatus = GetObjectStatus(pCommandStatus, pRelatedDimms[Index]->DeviceHandle.AsUint32); if (pObjectStatus != NULL) { if (LastNvmStatus == NVM_ERR_REGION_CURR_CONF_AFFECTS_UNSPEC_DIMM) { SetObjStatusForDimm(pCommandStatus, pRelatedDimms[Index], NVM_ERR_REGION_GOAL_CURR_CONF_AFFECTS_UNSPEC_DIMM); LastNvmStatus = NVM_ERR_REGION_GOAL_CURR_CONF_AFFECTS_UNSPEC_DIMM; } else { SetObjStatusForDimm(pCommandStatus, pRelatedDimms[Index], NVM_ERR_REGION_GOAL_CONF_AFFECTS_UNSPEC_DIMM); LastNvmStatus = NVM_ERR_REGION_GOAL_CONF_AFFECTS_UNSPEC_DIMM; } } else { SetObjStatusForDimm(pCommandStatus, pRelatedDimms[Index], NVM_ERR_REGION_GOAL_CONF_AFFECTS_UNSPEC_DIMM); LastNvmStatus = NVM_ERR_REGION_GOAL_CONF_AFFECTS_UNSPEC_DIMM; } } } } if (LastNvmStatus == NVM_ERR_REGION_CURR_CONF_AFFECTS_UNSPEC_DIMM || LastNvmStatus == NVM_ERR_REGION_GOAL_CONF_AFFECTS_UNSPEC_DIMM || LastNvmStatus == NVM_ERR_REGION_GOAL_CURR_CONF_AFFECTS_UNSPEC_DIMM) { Rc = EFI_INVALID_PARAMETER; goto Finish; } Rc = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** Find related dimms based on region configs relations @param[in] pDimms Input array of pointers to dimms @param[in] DimmsNum Number of pointers in pDimms @param[out] pRelatedDimms Output array of pointers to dimms @param[out] pRelatedDimmsNum Output number of found dimms @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ EFI_STATUS FindRelatedDimmsByRegions( IN DIMM *pDimms[], IN UINT32 DimmsNum, OUT DIMM *pRelatedDimms[MAX_DIMMS], OUT UINT32 *pRelatedDimmsNum ) { EFI_STATUS Rc = EFI_SUCCESS; UINT32 Index = 0; UINT32 Index2 = 0; VOID *pDimmPointer = NULL; LIST_ENTRY *pDimmRegionNode = NULL; DIMM_REGION *pDimmRegion = NULL; NVDIMM_ENTRY(); if (pDimms == NULL || pRelatedDimms == NULL || pRelatedDimmsNum == NULL || DimmsNum > MAX_DIMMS) { Rc = EFI_INVALID_PARAMETER; goto Finish; } *pRelatedDimmsNum = 0; for (Index = 0; Index < DimmsNum; Index++) { pRelatedDimms[Index] = pDimms[Index]; (*pRelatedDimmsNum) += 1; } for (Index = 0; Index < DimmsNum; Index++) { //only check if dimm has been configured by bios, otherwise //could be a dimm from a broken interleave set (moved from a different platform) if (DIMM_CONFIG_SUCCESS == pDimms[Index]->ConfigStatus) { for (Index2 = 0; Index2 < pDimms[Index]->ISsNum; Index2++) { LIST_FOR_EACH(pDimmRegionNode, &pDimms[Index]->pISs[Index2]->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pDimmRegionNode); pDimmPointer = pDimmRegion->pDimm; if (*pRelatedDimmsNum >= MAX_DIMMS) { NVDIMM_ERR("Found more Dimms than %d. Not possible in theory.", MAX_DIMMS); Rc = EFI_ABORTED; goto Finish; } else if (!IsPointerInArray((VOID **)pRelatedDimms, *pRelatedDimmsNum, pDimmPointer)) { ASSERT(*pRelatedDimmsNum < MAX_DIMMS); if (IsDimmManageable(pDimmPointer)) { pRelatedDimms[(*pRelatedDimmsNum)] = pDimmPointer; (*pRelatedDimmsNum) += 1; } } } } } } Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** Find related dimms based on region goal configs relations @param[in] pDimms Input array of pointers to dimms @param[in] DimmsNum Number of pointers in pDimms @param[out] pRelatedDimms Output array of pointers to dimms @param[out] pRelatedDimmsNum Output number of found dimms @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ EFI_STATUS FindRelatedDimmsByRegionGoalConfigs( IN DIMM *pDimms[], IN UINT32 DimmsNum, OUT DIMM *pRelatedDimms[MAX_DIMMS], OUT UINT32 *pRelatedDimmsNum ) { EFI_STATUS Rc = EFI_SUCCESS; UINT32 Index = 0; UINT32 Index2 = 0; UINT32 Index3 = 0; VOID *pDimmPointer = NULL; NVDIMM_ENTRY(); if (pDimms == NULL || pRelatedDimms == NULL || pRelatedDimmsNum == NULL || DimmsNum > MAX_DIMMS) { Rc = EFI_INVALID_PARAMETER; goto Finish; } *pRelatedDimmsNum = 0; for (Index = 0; Index < DimmsNum; Index++) { pRelatedDimms[(*pRelatedDimmsNum)] = pDimms[Index]; (*pRelatedDimmsNum) += 1; } for (Index = 0; Index < DimmsNum; Index++) { for (Index2 = 0; Index2 < pDimms[Index]->RegionsGoalNum; Index2++) { for (Index3 = 0; Index3 < pDimms[Index]->pRegionsGoal[Index2]->DimmsNum; Index3++) { pDimmPointer = (VOID *) pDimms[Index]->pRegionsGoal[Index2]->pDimms[Index3]; if (*pRelatedDimmsNum >= MAX_DIMMS) { NVDIMM_ERR("Found more Dimms than %d. Not possible in theory.", MAX_DIMMS); Rc = EFI_ABORTED; goto Finish; } else if (!IsPointerInArray((VOID **) pRelatedDimms, *pRelatedDimmsNum, pDimmPointer)) { ASSERT(*pRelatedDimmsNum < MAX_DIMMS); if (IsDimmManageable(pDimmPointer) && IsDimmInSupportedConfig(pDimmPointer)) { pRelatedDimms[(*pRelatedDimmsNum)] = pDimmPointer; (*pRelatedDimmsNum) += 1; } } } } } Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** Find an unique list of goal regions based on input list of dimms @param[in] pDimms Input array of pointers to dimms @param[in] DimmsNum Number of pointers in pDimms @param[out] pRegionsGoal Output array of pointers to REGION_GOAL @param[out] pRegionsGoalNum Output number of unique goal regions @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_ABORTED More config goals found than can exist **/ EFI_STATUS FindUniqueRegionsGoal( IN DIMM *pDimms[], IN UINT32 DimmsNum, OUT REGION_GOAL *pRegionsGoal[MAX_IS_CONFIGS], OUT UINT32 *pRegionsGoalNum ) { EFI_STATUS Rc = EFI_SUCCESS; UINT32 Index = 0; UINT32 Index2 = 0; VOID *pRegionGoalPointer = NULL; NVDIMM_ENTRY(); if (pDimms == NULL || pRegionsGoal == NULL || pRegionsGoalNum == NULL) { Rc = EFI_INVALID_PARAMETER; goto Finish; } *pRegionsGoalNum = 0; for (Index = 0; Index < DimmsNum; Index++) { for (Index2 = 0; Index2 < pDimms[Index]->RegionsGoalNum; Index2++) { pRegionGoalPointer = (VOID *) pDimms[Index]->pRegionsGoal[Index2]; if (*pRegionsGoalNum >= MAX_IS_CONFIGS) { NVDIMM_ERR("Found more regions than %d. Not possible in theory.", MAX_IS_CONFIGS); Rc = EFI_ABORTED; goto Finish; } else if (!IsPointerInArray((VOID **) pRegionsGoal, *pRegionsGoalNum, pRegionGoalPointer)) { pRegionsGoal[(*pRegionsGoalNum)] = pRegionGoalPointer; (*pRegionsGoalNum)++; } } } Finish: NVDIMM_EXIT_I64(Rc); return Rc; } EFI_STATUS ClearRegionsGoal( IN DIMM *pDimms[], IN UINT32 DimmsNum, IN REGION_GOAL **pRegionsGoal, IN UINT32 ExistingRegionsGoalNum ) { EFI_STATUS Rc = EFI_SUCCESS; UINT32 Index = 0; UINT32 Index2 = 0; UINT32 Index3 = 0; NVDIMM_ENTRY(); if (pDimms == NULL || pRegionsGoal == NULL) { Rc = EFI_INVALID_PARAMETER; goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { for (Index2 = 0; Index2 < pDimms[Index]->RegionsGoalNum; Index2++) { if (pDimms[Index]->pRegionsGoal[Index2] == pRegionsGoal[Index3]) { pDimms[Index]->pRegionsGoal[Index2] = NULL; } } } for (Index = 0; Index < ExistingRegionsGoalNum; Index++) { FreePool(pRegionsGoal[Index]); } Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** Check if the platform supports specified interleave sets. Also set default iMC and Channel interleave sizes if they are not specified. @param[in, out] pRegionGoal Array of pointers to regions goal @param[in] pRegionGoal Number of pointers in pRegionGoal @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS @retval EFI_LOAD_ERROR PCAT or its subtable not found @retval EFI_ABORTED Invalid region configuration, not supported by platform **/ EFI_STATUS VerifyInterleaveSetsPlatformSupport( IN OUT REGION_GOAL *pRegionGoal[], IN UINT32 RegionGoalNum, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Index = 0; UINT32 Index2 = 0; UINT64 Alignment = 0; BOOLEAN Found = FALSE; UINT8 iMCIntSize = 0; UINT8 ChannelIntSize = 0; UINT32 *pChannelInterleaveSize = NULL; UINT32 *piMCInterleaveSize = NULL; UINT32 *pRecommendedFormats = NULL; UINT32 *pChannelWays = NULL; UINT32 InterleaveAlignmentSize = 0; UINT32 InterleaveMapListLength = 0; UINT32 Length = 0; ACPI_REVISION Revision; NVDIMM_ENTRY(); if (pRegionGoal == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = RetrieveSupportediMcAndChannelInterleaveSizes(&pChannelInterleaveSize, &piMCInterleaveSize, &pRecommendedFormats, &pChannelWays, &Length, &InterleaveAlignmentSize, &Revision); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to retrieve supported iMc & Channel Interleave sizes from PCAT"); goto Finish; } if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(Revision)) { ReturnCode = RetrieveChannelWaysFromInterleaveSetMap(&pChannelWays, &InterleaveMapListLength); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to retrieve number of channel ways supported from Interleave Map"); goto Finish; } Length = InterleaveMapListLength; } if (pChannelWays == NULL) { NVDIMM_DBG("Interleave channel ways unknown!"); ReturnCode = EFI_ABORTED; goto Finish; } for (Index = 0; Index < RegionGoalNum; Index++) { Alignment = Pow(2, InterleaveAlignmentSize); /** Interleave set has to have a proper alignment **/ if (pRegionGoal[Index]->Size % (Alignment * pRegionGoal[Index]->DimmsNum) != 0) { NVDIMM_DBG("Interleave set lacking proper alignment defined by BIOS in PCAT tables."); ReturnCode = EFI_ABORTED; goto Finish; } if (pRegionGoal[Index]->Size == 0) { ResetCmdStatus(pCommandStatus, NVM_ERR_REGION_SIZE_TOO_SMALL_FOR_INT_SET_ALIGNMENT); ReturnCode = EFI_ABORTED; goto Finish; } Found = FALSE; for (Index2 = 0; Index2 < Length; Index2++) { if (!IS_BIT_SET_VAR(pRegionGoal[Index]->NumOfChannelWays, (UINT16)pChannelWays[Index2])) { continue; } if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(Revision)) { iMCIntSize = (UINT8)piMCInterleaveSize[0]; ChannelIntSize = (UINT8)pChannelInterleaveSize[0]; } else { iMCIntSize = (UINT8)piMCInterleaveSize[Index2]; ChannelIntSize = (UINT8)pChannelInterleaveSize[Index2]; } if (pRegionGoal[Index]->InterleaveSetType == DEFAULT_INTERLEAVE_SET_TYPE) { pRegionGoal[Index]->InterleaveSetType = INTERLEAVED; } if (pRegionGoal[Index]->ImcInterleaving == DEFAULT_IMC_INTERLEAVE_SIZE && pRegionGoal[Index]->ChannelInterleaving == DEFAULT_CHANNEL_INTERLEAVE_SIZE && ((IS_ACPI_REV_MAJ_0_MIN_VALID(Revision) && pRecommendedFormats != NULL) ? IS_BIT_SET_VAR(pRecommendedFormats[Index2], BIT0) : TRUE)) { pRegionGoal[Index]->ImcInterleaving = iMCIntSize; pRegionGoal[Index]->ChannelInterleaving = ChannelIntSize; Found = TRUE; break; } else if (pRegionGoal[Index]->ImcInterleaving == iMCIntSize && pRegionGoal[Index]->ChannelInterleaving == ChannelIntSize) { Found = TRUE; break; } } if (!Found) { if (pRegionGoal[Index]->ImcInterleaving == DEFAULT_IMC_INTERLEAVE_SIZE && pRegionGoal[Index]->ChannelInterleaving == DEFAULT_CHANNEL_INTERLEAVE_SIZE) { ResetCmdStatus(pCommandStatus, NVM_ERR_PLATFORM_NOT_SUPPORT_DEFAULT_INT_SIZES); } else { ResetCmdStatus(pCommandStatus, NVM_ERR_PLATFORM_NOT_SUPPORT_SPECIFIED_INT_SIZES); } ReturnCode = EFI_ABORTED; goto Finish; } } Finish: NVDIMM_EXIT_I64(ReturnCode); FREE_POOL_SAFE(pChannelInterleaveSize); FREE_POOL_SAFE(piMCInterleaveSize); FREE_POOL_SAFE(pRecommendedFormats); FREE_POOL_SAFE(pChannelWays); return ReturnCode; } /** Create REGION goal @param[in] pRegionGoalTemplate Pointer to REGION goal template @param[in] pDimms Array of pointers to DIMMs @param[in] DimmsNum Number of pointers in pDimms @param[in] InterleaveSetSize Interleave set size @param[in] pDriverPreferences Driver preferences for interleave sets Optional @param[in] SequenceIndex Variable to keep an order of REGIONs on DIMMs @param[in, out] pInterleaveSetIndex Unique index for interleave set @retval REGION_GOAL success @retval NULL one or more parameters are NULL or memory allocation failure **/ REGION_GOAL * CreateRegionGoal( IN REGION_GOAL_TEMPLATE *pRegionGoalTemplate, IN DIMM *pDimms[], IN UINT32 DimmsNum, IN UINT64 InterleaveSetSize, IN DRIVER_PREFERENCES *pDriverPreferences OPTIONAL, IN UINT16 SequenceIndex, IN OUT UINT16 *pInterleaveSetIndex ) { REGION_GOAL *pRegionGoal = NULL; UINT32 Index = 0; NVDIMM_ENTRY(); if (pRegionGoalTemplate == NULL || pDimms == NULL || pInterleaveSetIndex == NULL) { goto Finish; } pRegionGoal = AllocateZeroPool(sizeof(*pRegionGoal)); if (pRegionGoal == NULL) { goto Finish; } pRegionGoal->SequenceIndex = SequenceIndex; pRegionGoal->InterleaveSetType = pRegionGoalTemplate->InterleaveSetType; if (pRegionGoal->InterleaveSetType == NON_INTERLEAVED || pDriverPreferences == NULL) { pRegionGoal->ImcInterleaving = DEFAULT_IMC_INTERLEAVE_SIZE; pRegionGoal->ChannelInterleaving = DEFAULT_CHANNEL_INTERLEAVE_SIZE; } else { pRegionGoal->ImcInterleaving = pDriverPreferences->ImcInterleaving; pRegionGoal->ChannelInterleaving = pDriverPreferences->ChannelInterleaving; } /** @todo (WW21): There are additional restrictions on Interleave Set configurations. We decided with Software Management team that we should create two Interleave Sets, one on each iMC, if it is not possible to create one Interleave Set on socket (2 iMCs). Need to implement algorithm. **/ switch (DimmsNum) { case 1: pRegionGoal->NumOfChannelWays = INTERLEAVE_SET_1_WAY; break; case 2: pRegionGoal->NumOfChannelWays = INTERLEAVE_SET_2_WAY; break; case 3: pRegionGoal->NumOfChannelWays = INTERLEAVE_SET_3_WAY; break; case 4: pRegionGoal->NumOfChannelWays = INTERLEAVE_SET_4_WAY; break; case 6: pRegionGoal->NumOfChannelWays = INTERLEAVE_SET_6_WAY; break; case 8: pRegionGoal->NumOfChannelWays = INTERLEAVE_SET_8_WAY; break; case 12: pRegionGoal->NumOfChannelWays = INTERLEAVE_SET_12_WAY; break; case 16: pRegionGoal->NumOfChannelWays = INTERLEAVE_SET_16_WAY; break; case 24: pRegionGoal->NumOfChannelWays = INTERLEAVE_SET_24_WAY; break; default: NVDIMM_WARN("Unsupported number of DIMMs in interleave set: %d", DimmsNum); pRegionGoal->NumOfChannelWays = 0; break; } pRegionGoal->InterleaveSetIndex = *pInterleaveSetIndex; (*pInterleaveSetIndex)++; for (Index = 0; Index < DimmsNum; Index++) { pRegionGoal->pDimms[Index] = pDimms[Index]; } pRegionGoal->DimmsNum = DimmsNum; pRegionGoal->Size = InterleaveSetSize; Finish: NVDIMM_EXIT(); return pRegionGoal; } /** Get minimum and maximum sizes of AppDirect Namespace that can be created on the Interleave Set @param[in] pIS Interleave Set that sizes of AppDirect Namespaces will be determined for @param[out] pMinSize Output parameter for minimum size @param[out] pMaxSize Output parameter for maximum size @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ EFI_STATUS ADNamespaceMinAndMaxAvailableSizeOnIS( IN NVM_IS *pIS, OUT UINT64 *pMinSize, OUT UINT64 *pMaxSize ) { EFI_STATUS ReturnCode = EFI_SUCCESS; MEMMAP_RANGE AppDirectRange; UINT32 RegionCount = 0; ZeroMem(&AppDirectRange, sizeof(AppDirectRange)); NVDIMM_ENTRY(); if (pIS == NULL || pMinSize == NULL || pMaxSize == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pMinSize = 0; *pMaxSize = 0; ReturnCode = GetListSize(&pIS->DimmRegionList, &RegionCount); if (EFI_ERROR(ReturnCode) || RegionCount == 0) { goto Finish; } ReturnCode = FindADMemmapRangeInIS(pIS, MAX_UINT64_VALUE, &AppDirectRange); if (EFI_ERROR(ReturnCode) && ReturnCode != EFI_NOT_FOUND) { goto Finish; } ReturnCode = EFI_SUCCESS; *pMinSize = gNvmDimmData->Alignments.PmNamespaceMinSize * RegionCount; *pMaxSize = AppDirectRange.RangeLength * RegionCount; if (*pMinSize > *pMaxSize) { *pMinSize = 0; *pMaxSize = 0; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Verify that all DIMMs with goal config are specified on a given socket at once to keep supported region configs. @param[in] pDimms List of DIMMs to configure @param[in] DimmsNum Number of DIMMs to configure @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER if one or more parameters are NULL @retval EFI_UNSUPPORTED A given config is unsupported **/ EFI_STATUS VerifyDeletingSupportedRegionConfigs( IN DIMM *pDimms[], IN UINT32 DimmsNum, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS Rc = EFI_SUCCESS; UINT32 DimmsWithGoalConfigNum = 0; UINT32 SpecifiedDimmsWithGoalConfigNum = 0; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; UINT32 Socket = 0; UINT32 Index = 0; NVDIMM_ENTRY(); if (pDimms == NULL || pCommandStatus == NULL) { Rc = EFI_INVALID_PARAMETER; goto Finish; } for (Socket = 0; Socket < MAX_SOCKETS; Socket++) { DimmsWithGoalConfigNum = 0; /** Get a number of all Dimms with goal config on a given socket **/ LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (!IsDimmManageable(pDimm)){ continue; } if (Socket == pDimm->SocketId) { if (pDimm->RegionsGoalConfig) { DimmsWithGoalConfigNum++; } } } SpecifiedDimmsWithGoalConfigNum = 0; /** Get a number of specified DIMMs with goal config on a given socket **/ for (Index = 0; Index < DimmsNum; Index++) { if (Socket == pDimms[Index]->SocketId) { if (pDimms[Index]->RegionsGoalConfig) { SpecifiedDimmsWithGoalConfigNum++; } } } /** If any DIMM is specified for a given socket then all DIMMs with goal config have to be specified **/ if (!(SpecifiedDimmsWithGoalConfigNum == 0 || SpecifiedDimmsWithGoalConfigNum == DimmsWithGoalConfigNum)) { Rc = EFI_UNSUPPORTED; ResetCmdStatus(pCommandStatus, NVM_ERR_REGION_CONF_UNSUPPORTED_CONFIG); goto Finish; } } Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** Find Region Goal Dimm in an array @param[in] pDimms Array that will be searched @param[in] DimmsNum Number of items in pDimms @param[in] pDimmToFind Dimm to find @retval Region Goal Dimm pointer - if found @retval NULL - if not found **/ REGION_GOAL_DIMM * FindRegionGoalDimm( IN REGION_GOAL_DIMM *pDimms, IN UINT32 DimmsNum, IN DIMM *pDimmToFind ) { REGION_GOAL_DIMM *pRegionGoalDimm = NULL; UINT32 Index = 0; if (pDimms == NULL || pDimmToFind == NULL) { goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { if (pDimms[Index].pDimm == pDimmToFind) { pRegionGoalDimm = &pDimms[Index]; break; } } Finish: return pRegionGoalDimm; } /** Clear all internal goal configurations structures @param[in, out] pDimmList Head of the list of all NVM DIMMs in the system @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS ClearInternalGoalConfigsInfo( IN OUT LIST_ENTRY *pDimmList ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LIST_ENTRY *pDimmNode = NULL; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; REGION_GOAL *pRegionsGoal[MAX_IS_CONFIGS]; UINT32 RegionsGoalNum = 0; UINT32 Index = 0; UINT32 Index2 = 0; NVDIMM_ENTRY(); SetMem(pDimms, sizeof(pDimms), 0x0); SetMem(pRegionsGoal, sizeof(pRegionsGoal), 0x0); if (pDimmList == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /** Copy list to array **/ DimmsNum = 0; LIST_FOR_EACH(pDimmNode, pDimmList) { ASSERT(DimmsNum < MAX_DIMMS); pDimms[DimmsNum] = DIMM_FROM_NODE(pDimmNode); DimmsNum++; } ReturnCode = FindUniqueRegionsGoal(pDimms, DimmsNum, pRegionsGoal, &RegionsGoalNum); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error in FindUniqueRegionsGoal"); goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { pDimms[Index]->RegionsGoalConfig = FALSE; for (Index2 = 0; Index2 < MAX_IS_PER_DIMM; Index2++) { pDimms[Index]->pRegionsGoal[Index2] = NULL; } pDimms[Index]->RegionsGoalNum = 0; pDimms[Index]->VolatileSizeGoal = 0; pDimms[Index]->GoalConfigStatus = GOAL_CONFIG_STATUS_NO_GOAL_OR_SUCCESS; pDimms[Index]->PcdSynced = FALSE; } for (Index = 0; Index < RegionsGoalNum; Index++) { FREE_POOL_SAFE(pRegionsGoal[Index]); } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Calculate actual volatile size @param[in] RawDimmCapacity Raw capacity to calculate actual volatile size for @param[in] VolatileSizeRounded @param[out] pVolatileSizeActual Actual Volatile region size - actual volatile size with metadata @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ EFI_STATUS CalculateActualVolatileSize( IN UINT64 RawDimmCapacity, IN UINT64 VolatileSizeRequested, OUT UINT64 *pVolatileSizeActual ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT64 TempPersistentSize = 0; UINT64 UnalignedPersistentSize = 0; NVDIMM_ENTRY(); if (pVolatileSizeActual == NULL) { goto Finish; } // If user requests more than 0 but less than the minimum amount, round up to the minimum for them if (VolatileSizeRequested != 0 && VolatileSizeRequested < gNvmDimmData->Alignments.RegionVolatileAlignment) { VolatileSizeRequested = ROUNDUP(VolatileSizeRequested, gNvmDimmData->Alignments.RegionVolatileAlignment); } if (RawDimmCapacity <= VolatileSizeRequested) { *pVolatileSizeActual = RawDimmCapacity; } else if (VolatileSizeRequested == 0) { *pVolatileSizeActual = 0; } else { TempPersistentSize = RawDimmCapacity - VolatileSizeRequested; // alignment calculation here is done at socket level UnalignedPersistentSize = TempPersistentSize % gNvmDimmData->Alignments.RegionPartitionAlignment; if (UnalignedPersistentSize > (gNvmDimmData->Alignments.RegionPartitionAlignment / 2)) { TempPersistentSize = ROUNDUP(TempPersistentSize, gNvmDimmData->Alignments.RegionPartitionAlignment); } else { TempPersistentSize = ROUNDDOWN(TempPersistentSize, gNvmDimmData->Alignments.RegionPartitionAlignment); // If we round down to 0 the user would receive 100% volatile and they had requested at least a little persistent if (TempPersistentSize == 0) { TempPersistentSize = gNvmDimmData->Alignments.RegionPartitionAlignment; } } // Always give the user some amount of volatile if they have requested it if (TempPersistentSize >= RawDimmCapacity && VolatileSizeRequested != 0) { *pVolatileSizeActual = RawDimmCapacity - (TempPersistentSize - gNvmDimmData->Alignments.RegionPartitionAlignment); } else if (TempPersistentSize >= RawDimmCapacity) { *pVolatileSizeActual = 0; } else { *pVolatileSizeActual = RawDimmCapacity - TempPersistentSize; } } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check for new goal configs for the DIMM @param[in] pDIMM The current DIMM @param[out] pHasNewGoal TRUE if any of the dimms have a new goal, else FALSE @retval EFI_SUCCESS #retval EFI_INVALID_PARAMETER If IS is null **/ EFI_STATUS FindIfNewGoalOnDimm( IN DIMM *pDimm, OUT BOOLEAN *pHasNewGoal ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if ((pDimm == NULL) || (pHasNewGoal == NULL)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pHasNewGoal = FALSE; if (pDimm->GoalConfigStatus == GOAL_CONFIG_STATUS_NEW) { *pHasNewGoal = TRUE; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check if security state of specified DIMM is locked @param[in] pDimm The current DIMM @param[out] pIsLocked TRUE if security state of specified dimm is locked @retval EFI_SUCCESS #retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Allocation failed **/ EFI_STATUS IsDimmLocked( IN DIMM *pDimm, OUT BOOLEAN *pIsLocked ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PT_GET_SECURITY_PAYLOAD *pSecurityPayload = NULL; UINT8 SecurityState = SECURITY_UNKNOWN; NVDIMM_ENTRY(); if ((pDimm == NULL) || (pIsLocked == NULL)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pIsLocked = FALSE; pSecurityPayload = AllocateZeroPool(sizeof(*pSecurityPayload)); if (pSecurityPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = FwCmdGetSecurityInfo(pDimm, pSecurityPayload); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FW CMD Error: " FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } ConvertSecurityBitmask(pSecurityPayload->SecurityStatus.AsUint32, &SecurityState); /** If any of the DIMM on the IS is locked, then break **/ if (SecurityState == SECURITY_LOCKED) { *pIsLocked = TRUE; } Finish: FREE_POOL_SAFE(pSecurityPayload); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Set Interleave Set state taking states' priority into account @param[in] CurrentState Current IS state @param[in] NewState IS state to be set @retval UINT8 New IS state **/ UINT8 SetISStateWithPriority( IN UINT8 CurrentState, IN UINT8 NewState ) { return CurrentState > NewState ? CurrentState : NewState; } /** Check for existing goal configs on a socket for which a new goal config has been requested @param[in] pDimms Array of pointers to DIMMs based on the goal config requested @param[in] pDimmsNum Number of pointers in pDimms @retval EFI_ABORTED one or more DIMMs on a socket already have goal configs @retval EFI_INVALID_PARAMETER pDimms or pDimmsNum is NULL **/ EFI_STATUS CheckForExistingGoalConfigPerSocket( IN DIMM *pDimms[MAX_DIMMS], IN UINT32 *pDimmsNum ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LIST_ENTRY *pDimmNode = NULL; DIMM *pDimm = NULL; UINT32 Socket = MAX_UINT32_VALUE; UINT32 PrevSocket = MAX_UINT32_VALUE; UINT32 Index = 0; if (pDimms == NULL || pDimmsNum == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } for (Index = 0; Index < *pDimmsNum; Index++) { Socket = pDimms[Index]->SocketId; if (PrevSocket == Socket) { continue; } LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (!IsDimmManageable(pDimm) || pDimm->NonFunctional || (Socket != pDimm->SocketId)) { continue; } if (pDimm->GoalConfigStatus != GOAL_CONFIG_STATUS_NO_GOAL_OR_SUCCESS) { ReturnCode = EFI_ABORTED; goto Finish; } } PrevSocket = Socket; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Examines the system topology for the system DDR capacity and compares it to the 2LM capacity to check for ratio violations @param[in] SocketId Socket Id, value 0xFFFF indicates include all socket values @param[in] pDimmsSym Array of Dimms for symmetrical region config @param[in] DimmsSymNum Number of items in DimmsSym @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS CheckNmFmLimits( IN UINT16 SocketId, IN REGION_GOAL_DIMM *pDimmsSym, IN UINT32 DimmsSymNum, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT64 TwoLM_FmMinRecommended = 0; UINT64 TwoLM_FmMaxRecommended = 0; UINT64 TwoLM_NMTotal = 0; UINT64 TwoLM_FMTotal = 0; MEMORY_MODE AllowedMode = MEMORY_MODE_1LM; UINT32 Index; NvmStatusCode LowerRatioViolation; NvmStatusCode UpperRatioViolation; UINT16 SubsystemDeviceId; NVDIMM_ENTRY(); if (pDimmsSym == NULL || pCommandStatus == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Get total PMem module volatile capacity (Far Memory) ReturnCode = CalculateFarMemorySizeForNewGoalConfigs(SocketId, pDimmsSym, DimmsSymNum, &TwoLM_FMTotal, pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Could not calculate far memory capacity."); goto Finish; } if (TwoLM_FMTotal == 0) { //no limit check necessary - no 2LM goal in play ReturnCode = EFI_SUCCESS; goto Finish; } CHECK_RESULT(AllowedMemoryMode(&AllowedMode), Finish); // Get total usable DDR Cache Size ReturnCode = GetTotalUsableDDRCacheSize(SocketId, AllowedMode, &TwoLM_NMTotal); if (EFI_ERROR(ReturnCode)) { // If not Purley, this is an error. On Purley we expect the PMTT table // to be missing on some platforms, workarounds are further below if (!IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr)) { // Preserve return code and error out NVDIMM_DBG("Could not retrieve usable DDR cache size"); goto Finish; } // We can't determine the cache size (and it's non-zero), likely because the PMTT // table is missing. Since this is an allowed condition at this point, show a // a warning to the user and return success. ReturnCode = EFI_SUCCESS; SetCmdStatus(pCommandStatus, NVM_WARN_PMTT_TABLE_NOT_FOUND); goto Finish; } if (TwoLM_NMTotal > TwoLM_FMTotal) { NVDIMM_ERR("NM:FM ratio violated and is greater than 1."); ResetCmdStatus(pCommandStatus, NVM_ERR_NMFM_RATIO_GREATER_THAN_ONE); ReturnCode = EFI_UNSUPPORTED; goto Finish; } SubsystemDeviceId = SPD_DEVICE_ID_20; // default to latest for (Index = 0; Index < DimmsSymNum; ++Index) { if (SocketId == pDimmsSym[Index].pDimm->SocketId) { SubsystemDeviceId = pDimmsSym[Index].pDimm->SubsystemDeviceId; break; } } // For older devices use the ratio values that they were originally designed for if ((SPD_DEVICE_ID_10 == SubsystemDeviceId) || (SPD_DEVICE_ID_15 == SubsystemDeviceId)) { TwoLM_FmMinRecommended = (UINT64)(TwoLM_NMTotal * TWOLM_NMFM_RATIO_LOWER_3_6); TwoLM_FmMaxRecommended = TwoLM_NMTotal * TWOLM_NMFM_RATIO_UPPER_16; LowerRatioViolation = NVM_WARN_NMFM_RATIO_LOWER_VIOLATION_1to3_6; UpperRatioViolation = NVM_WARN_NMFM_RATIO_UPPER_VIOLATION_1to16; } else { TwoLM_FmMinRecommended = (UINT64)(TwoLM_NMTotal * TWOLM_NMFM_RATIO_LOWER_2); TwoLM_FmMaxRecommended = TwoLM_NMTotal * TWOLM_NMFM_RATIO_UPPER_8; LowerRatioViolation = NVM_WARN_NMFM_RATIO_LOWER_VIOLATION_1to2; UpperRatioViolation = NVM_WARN_NMFM_RATIO_UPPER_VIOLATION_1to8; } if (TwoLM_FMTotal > TwoLM_FmMaxRecommended) { SetCmdStatus(pCommandStatus, UpperRatioViolation); } else if (TwoLM_FMTotal < TwoLM_FmMinRecommended) { SetCmdStatus(pCommandStatus, LowerRatioViolation); } Finish: if ((pCommandStatus != NULL) && EFI_ERROR(ReturnCode) && (pCommandStatus->GeneralStatus == NVM_ERR_OPERATION_NOT_STARTED)) { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Checks if all DIMMs in the list are in configured state @param[IN] pDimmList Head of the Dimm list @param[IN] pDimmsUnConfigured Boolean flag to indicate if any PMem module is unconfigured @param[OUT] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER if input parameter null **/ EFI_STATUS CheckIfAllDimmsConfigured( IN LIST_ENTRY *pDimmList, OUT BOOLEAN *pDimmsUnConfigured, OUT COMMAND_STATUS *pCommandStatus OPTIONAL ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; NVDIMM_ENTRY(); if (pDimmList == NULL || pDimmsUnConfigured == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } LIST_FOR_EACH(pDimmNode, pDimmList) { pDimm = DIMM_FROM_NODE(pDimmNode); if (!IsDimmManageable(pDimm) || DIMM_MEDIA_NOT_ACCESSIBLE(pDimm->BootStatusBitmask)) { continue; } if (pDimm->ConfigStatus == DIMM_CONFIG_UNDEFINED) { if (pCommandStatus != NULL) { ResetCmdStatus(pCommandStatus, NVM_ERR_PCD_CURR_CONF_MISSING); } *pDimmsUnConfigured = TRUE; goto Finish; } } *pDimmsUnConfigured = FALSE; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Calculate total far memory on PMem modules for existing goal configs @param[in] SocketId Socket Id, value 0xFFFF indicates include all socket values @param[in] pDimmsSym Array of Dimms for symmetrical region config @param[in] DimmsSymNum Number of items in DimmsSym @param[out] pTotalFarMemorySize Pointer to total far memory capacity @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER if input parameter null **/ EFI_STATUS CalculateFarMemorySizeForNewGoalConfigs( IN UINT16 SocketId, IN REGION_GOAL_DIMM *pDimmsSym, IN UINT32 DimmsSymNum, OUT UINT64 *pTotalFarMemorySize, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM **ppDimms = NULL; UINT32 DimmsNum = 0; UINT64 VolatileCapacityUnspecifiedDimms = 0; UINT32 Index1 = 0; UINT32 Index2 = 0; UINT32 NumOfUnspecifiedDimms = 0; UINT16 *pSocketIds = NULL; UINT32 SocketsNum = 0; BOOLEAN UnSpecifiedDimm = TRUE; REQUIRE_DCPMMS RequireDcpmmsBitfield = REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_FUNCTIONAL | REQUIRE_DCPMMS_NO_POPULATION_VIOLATION; NVDIMM_ENTRY(); if (pDimmsSym == NULL || pTotalFarMemorySize == NULL || pCommandStatus == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Set the default value to 0 *pTotalFarMemorySize = 0; for (Index1 = 0; Index1 < DimmsSymNum; Index1++) { if (SocketId == SOCKET_ID_ALL || pDimmsSym[Index1].pDimm->SocketId == SocketId) { *pTotalFarMemorySize += ROUNDDOWN(pDimmsSym[Index1].VolatileSize, gNvmDimmData->Alignments.RegionVolatileAlignment); } } if (*pTotalFarMemorySize == 0) { // No 2LM goal in play goto Finish; } ppDimms = AllocateZeroPool(sizeof(*ppDimms) * MAX_DIMMS); if (ppDimms == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } /** Initialize socket ID array and array length input parameters for VerifyTargetDimms function when a single socket ID is specified. **/ if (SocketId != SOCKET_ID_ALL) { pSocketIds = &SocketId; SocketsNum = 1; } ReturnCode = VerifyTargetDimms(NULL, 0, pSocketIds, SocketsNum, RequireDcpmmsBitfield, ppDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } // If all usable PMem modules targeted, then skip further calculations if (DimmsSymNum == DimmsNum) { goto Finish; } /** Targeting a subset of PMem modules for 100% MemoryMode is allowed if all unspecified PMem modules are configured for 100% MemoryMode only. Account for volatile partitions on unspecified PMem modules in this case. **/ for (Index1 = 0; Index1 < DimmsNum; Index1++) { for (Index2 = 0; Index2 < DimmsSymNum; Index2++) { if ((SocketId == SOCKET_ID_ALL || pDimmsSym[Index2].pDimm->SocketId == SocketId) && (pDimmsSym[Index2].pDimm->DeviceHandle.AsUint32 == ppDimms[Index1]->DeviceHandle.AsUint32)) { UnSpecifiedDimm = FALSE; break; } } if (UnSpecifiedDimm && ppDimms[Index1]->VolatileCapacity > 0 && ppDimms[Index1]->ISsNfitNum == 0) { NumOfUnspecifiedDimms++; VolatileCapacityUnspecifiedDimms += ROUNDDOWN(ppDimms[Index1]->VolatileCapacity, gNvmDimmData->Alignments.RegionVolatileAlignment); } UnSpecifiedDimm = TRUE; } if ((DimmsSymNum + NumOfUnspecifiedDimms) == DimmsNum) { *pTotalFarMemorySize += VolatileCapacityUnspecifiedDimms; } Finish: FREE_POOL_SAFE(ppDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; }ipmctl-03.00.00.0485/DcpmPkg/driver/Core/Region.h000066400000000000000000001054151440615110200207250ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _REGION_H_ #define _REGION_H_ #include #include #include #include #include #include #include #define INTERLEAVE_WAYS_X1 1 #define INTERLEAVE_WAYS_X2 2 #define INTERLEAVE_WAYS_X3 3 #define INTERLEAVE_WAYS_X4 4 #define INTERLEAVE_WAYS_X8 8 #define INTERLEAVE_WAYS_X12 12 #define INTERLEAVE_WAYS_X16 16 #define INTERLEAVE_WAYS_X24 24 /** IS_STATE value indicates its priority (0 is lowest) **/ #define IS_STATE_HEALTHY 0 #define IS_STATE_SPA_MISSING 1 #define IS_STATE_CONFIG_INACTIVE 2 #define IS_STATE_DIMM_MISSING 3 #define IS_STATE_INIT_FAILURE 4 struct _NAMESPACE; typedef struct _DIMM_REGION { LIST_ENTRY DimmRegionNode; UINT64 Signature; DIMM *pDimm; UINT64 PartitionOffset; UINT64 PartitionSize; UINT64 SpaRegionOffset; } DIMM_REGION; #define DIMM_REGION_SIGNATURE SIGNATURE_64('D', 'I', 'M', 'M', 'R', 'E', 'O', 'N') #define DIMM_REGION_FROM_NODE(a) CR(a, DIMM_REGION, DimmRegionNode, DIMM_REGION_SIGNATURE) typedef struct _NVM_IS { LIST_ENTRY IsNode; UINT64 Signature; UINT16 SocketId; //!< Identifies the processor socket containing the DCPMM UINT16 InterleaveSetIndex; UINT16 RegionId; //!< Used to uniquely identify regions as InterleaveSetIndex is not unique enough UINT64 Size; //!< Current total capacity of the Interleave Set /** bit0 set - IS_STATE_INIT_FAILURE - Interleave Set or dimm region (one or more) initialization failure bit1 set - IS_STATE_DIMM_MISSING - dimm missing (serial number of dimm from the Platform Config Data not found in the dimm list) **/ UINT8 State; UINT16 InterleaveFormatChannel; UINT16 InterleaveFormatImc; UINT16 InterleaveFormatWays; LIST_ENTRY DimmRegionList; SpaRangeTbl *pSpaTbl; LIST_ENTRY AppDirectNamespaceList; UINT64 InterleaveSetCookie; UINT64 InterleaveSetCookie_1_1; } NVM_IS; #define IS_SIGNATURE SIGNATURE_64('I', 'N', 'T', 'S', '_', 'S', 'I', 'G') #define IS_FROM_NODE(a) CR(a, NVM_IS, IsNode, IS_SIGNATURE) typedef struct _NVM_REGION { LIST_ENTRY RegionNode; UINT64 Signature; UINT16 RegionId; UINT16 Socket; UINT8 Type; DIMM *pDimmsBlockOnly[MAX_DIMMS_PER_SOCKET]; UINT32 DimmsBlockOnlyNum; UINT64 BlockOnlySize; NVM_IS *pISs[MAX_IS_PER_SOCKET]; UINT32 ISsNum; UINT64 ISsSize; UINT64 VolatileSize; } NVM_REGION; typedef struct _REGION_GOAL_DIMM { DIMM *pDimm; UINT64 RegionSize; UINT64 VolatileSize; } REGION_GOAL_DIMM; #define NVM_REGION_SIGNATURE SIGNATURE_64('R', 'E', 'G', 'I', '_', 'S', 'I', 'G') #define NVM_REGION_FROM_NODE(a) CR(a, NVM_REGION, RegionNode, NVM_REGION_SIGNATURE) typedef struct _REGION_GOAL { UINT32 SequenceIndex; //!< Variable to keep an order of REGIONS on DIMMs UINT64 Size; //!< Size of the pool in bytes UINT8 InterleaveSetType; //!< Type of interleave set: non-interleaved, interleaved UINT8 ImcInterleaving; //!< IMC interleaving as bit field UINT8 ChannelInterleaving; //!< Channel interleaving as bit field UINT16 NumOfChannelWays; //!< Number of channel ways as bit field UINT16 InterleaveSetIndex; //!< Logical index number, it should be the same for all the DIMMs in the interleave set DIMM *pDimms[MAX_DIMMS_PER_SOCKET]; UINT32 DimmsNum; } REGION_GOAL; /** Allocate and initialize the Interleave Set by using NFIT table @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pNvDimmRegionMappingStructure The NVDIMM region that helps describe this region of memory @param[in] RegionId The next consecutive region id @param[out] ppIS Interleave Set parent for new dimm region @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS InitializeISFromNfit( IN ParsedFitHeader *pFitHead, IN NvDimmRegionMappingStructure *pNvDimmRegionTbl, IN UINT16 RegionId, OUT NVM_IS **ppIS ); /** Allocate and initialize the Interleave Set by using Interleave Information table from Platform Config Data @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS InitializeIS( IN VOID *pInterleaveInfoTable, IN UINT16 RegionId, IN ACPI_REVISION PcdConfRevision, OUT NVM_IS **ppIS ); /** Create and initialize all Interleave Sets. When something goes wrong with particular Interleave Set then no additional Interleave Set structs created or error state on Interleave Set is set. @param[in] pFitHead NVM Firmware Interface Table @param[in] pDimmList Head of the list of all NVM DIMMs in the system @param[out] pISList Head of the list for Interleave Sets @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS InitializeISs( IN ParsedFitHeader *pFitHead, IN LIST_ENTRY *pDimmList, IN BOOLEAN UseNfit, OUT LIST_ENTRY *pISList ); /** Initialize interleave sets It initializes the interleave sets using NFIT or PCD @param[in] UseNfit Flag to indicate usage of NFIT or else default to PCD @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS InitializeInterleaveSets( IN BOOLEAN UseNfit ); /** Determine Region Type based on the Interleave sets @param[in, out] pRegion The region whose type needs to be determined @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ EFI_STATUS DetermineRegionType( IN NVM_IS *pRegion, OUT UINT8 *pRegionType ); /** Get Region by ID Scan the Region list for a Region identified by ID @param[in] pRegionList Head of the list for Regions @param[in] RegionId Region identifier @retval NVM_IS struct pointer if matching Region has been found @retval NULL pointer if not found **/ NVM_IS * GetRegionById( IN LIST_ENTRY *pRegionList, IN UINT16 RegionId ); /** Get Region List Returns the pointer to the region list. It is also initializing the region list if it is necessary. @param[in] pRegionList Head of the list for Regions @param[in] UseNfit Flag to indicate usage of NFIT @retval pointer to the region list **/ EFI_STATUS GetRegionList( IN LIST_ENTRY **ppRegionList, IN BOOLEAN UseNfit ); /** Clean the Interleave Set @param[in, out] pDimmList: the list of DCPMMs @param[in, out] pISList: the list of Interleave Sets to clean **/ VOID CleanISLists( IN OUT LIST_ENTRY *pDimmList, IN OUT LIST_ENTRY *pISList ); /** Free a Interleave Set and all memory resources in use by the Interleave Set. @param[in, out] pIS the Interleave Set and its regions that will be released **/ VOID FreeISResources( IN OUT NVM_IS *pIS ); /** Allocate and initialize the DIMM region by using NFIT table @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pDimm Target DIMM structure pointer @param[in] pISList List of interleave sets formed so far @param[in] pNvDimmRegionMappingStructure The NVDIMM region that helps describe this region of memory @param[out] pRegionId The next consecutive region id @param[out] ppNewIS Interleave Set parent for new dimm region @param[out] ppDimmRegion new allocated dimm region will be put here @param[out] pISDimmRegionAlreadyExists TRUE if Interleave Set DIMM region already exists @retval EFI_SUCCESS @retval EFI_NOT_FOUND the Dimm related with DimmRegion has not been found on the Dimm list @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS InitializeDimmRegionFromNfit( IN ParsedFitHeader *pFitHead, IN DIMM *pDimm, IN LIST_ENTRY *pISList, IN NvDimmRegionMappingStructure *pNvDimmRegionMappingStructure, OUT UINT16 *pRegionId, OUT NVM_IS **ppCurrentIS, OUT DIMM_REGION **ppDimmRegion, OUT BOOLEAN *pISDimmRegionAlreadyExists ); /** Allocate and initialize the dimm region by using Interleave Information table from Platform Config Data @param[in] pCurDimm the DIMM from which Interleave Information table was retrieved @param[in] pDimmList Head of the list of all Intel NVM Dimm in the system @param[in] pISList List of interleave sets formed so far @param[in] pIdentificationInfoTable Identification Information table @param[in] pInterleaveInfoTable Interleave information for the particular dimm @param[in] PcdConfRevision Revision of the PCD Config tables @param[out] pRegionId The next consecutive region id @param[out] ppNewIS Interleave Set parent for new dimm region @param[out] ppDimmRegion new allocated dimm region will be put here @param[out] pISAlreadyExists TRUE if Interleave Set already exists @retval EFI_SUCCESS @retval EFI_NOT_FOUND the Dimm related with DimmRegion has not been found on the Dimm list @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS InitializeDimmRegion( IN DIMM *pCurDimm, IN LIST_ENTRY *pDimmList, IN LIST_ENTRY *pISList, IN VOID *pIdentificationInfoTable, IN VOID *pInterleaveInfoTable, IN ACPI_REVISION PcdConfRevision, OUT UINT16 *pRegionId, OUT NVM_IS **ppNewIS, OUT DIMM_REGION **ppDimmRegion, OUT BOOLEAN *pISAlreadyExists ); /** Retrieve Interleave Sets by using NFIT table Using the parsed NFIT table data to get information about Interleave Sets configuration. @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pDimmList Head of the list of all NVM DIMMs in the system @param[out] pISList Head of the list for Interleave Sets @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RetrieveISsFromNfit( IN ParsedFitHeader *pFitHead, IN LIST_ENTRY *pDimmList, OUT LIST_ENTRY *pISList ); /** Retrieve Interleave Sets by using Platform Config Data from Intel NVM Dimms Using the Platform Config Data command to get information about Interleave Sets configuration. @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pDimmList Head of the list of all NVM DIMMs in the system @param[out] pISList Head of the list for Interleave Sets @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RetrieveISsFromPlatformConfigData( IN ParsedFitHeader *pFitHead, IN LIST_ENTRY *pDimmList, OUT LIST_ENTRY *pISList ); /** Parse Interleave Information table and create a Interleave Set if it doesn't exist yet. @param[in] pFitHead Fully populated NVM Firmware Interface Table @param[in] pDimmList Head of the list of all Intel NVM Dimms in the system @param[in] pInterleaveInfo Interleave Information table retrieve from DIMM @param[in] PcdCurrentConfRevision PCD Current Config table revision @param[in] pDimm the DIMM from which Interleave Information table was retrieved @param[in out] pRegionId Unique id for region @param[out] pISList Head of the list for Interleave Sets @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RetrieveISFromInterleaveInformationTable( IN ParsedFitHeader *pFitHead, IN LIST_ENTRY *pDimmList, IN VOID *pInterleaveInfoTable, IN ACPI_REVISION PcdCurrentConfRevision, IN DIMM *pDimm, IN OUT UINT16 *pRegionId, OUT LIST_ENTRY *pISList ); /** Clear previous regions goal configs and - if regions goal configs is specified - replace them with new one. 1. Clear previous regions goal configs on all affected dimms 2. [OPTIONAL] Send new regions goal configs to dimms 3. Set information about synchronization with dimms @param[in] pDimmList Head of the list of all NVM DIMMs in the system @param[in] DimmsNum Number of dimms in pDimmList @param[in] ReservedSizeIsZero Indicate whether the reserved size is zero @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pDimmList is NULL @retval return codes from SendConfigInputToDimm **/ EFI_STATUS ApplyGoalConfigsToDimms( IN DIMM **ppDimms, IN UINT32 DimmsNum, IN BOOLEAN ReservedSizeIsZero, OUT COMMAND_STATUS *pCommandStatus ); /** Send new Configuration Input to dimm. Get Platform Config Data from dimm, replace Configuration Input with new one, and send it back to dimm. If pNewConfigInput is NULL, then the function will send Platform Config Data without Configuration Input (old one will be removed). @param[in] pDimm dimm that we replace Platform Config Data for @param[in] pNewConfigInput new config input for dimm @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER pDimm is NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval other error codes from called functions: FwCmdGetPlatformConfigData, FwCmdSetPlatformConfigData **/ EFI_STATUS SendConfigInputToDimm( IN DIMM *pDimm, IN NVDIMM_PLATFORM_CONFIG_INPUT *pNewConfigInput OPTIONAL ); /** Verify that all specified features in goal config are supported by platform @param[in] VolatileSize Volatile region size @param[in] PersistentMemType Persistent memory type @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER If one or more parameters are NULL @retval EFI_UNSUPPORTED A given config is unsupported @retval EFI_LOAD_ERROR There is no PCAT **/ EFI_STATUS VerifyPlatformSupport( IN UINT64 VolatileSize, IN UINT8 PersistentMemType, OUT COMMAND_STATUS *pCommandStatus ); /** Check if the platform supports specified interleave sets. Also set default iMC and Channel interleave sizes if they are not specified. @param[in, out] pRegionGoal Array of pointers to pools goal @param[in] pRegionGoal Number of pointers in pRegionGoal @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS @retval EFI_LOAD_ERROR PCAT or its subtable not found @retval EFI_ABORTED Invalid pool configuration, not supported by platform **/ EFI_STATUS VerifyInterleaveSetsPlatformSupport( IN OUT REGION_GOAL *pRegionGoal[], IN UINT32 RegionGoalNum, OUT COMMAND_STATUS *pCommandStatus ); /** Determine Regions health based on health state of Interleave Sets @param[in] pRegion The pool whose health is to be determined @param[out] pHealthState The health state of the pool @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Could not allocate memory **/ EFI_STATUS DetermineRegionHealth( IN NVM_IS *pRegion, OUT UINT16 *pHealthState ); /** Get minimum and maximum sizes of AppDirect Namespace that can be created on the Interleave Set @param[in] pIS Interleave Set that sizes of AppDirect Namespaces will be determined for @param[out] pMinSize Output parameter for minimum size @param[out] pMaxSize Output parameter for maximum size @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ EFI_STATUS ADNamespaceMinAndMaxAvailableSizeOnIS( IN NVM_IS *pIS, OUT UINT64 *pMinSize, OUT UINT64 *pMaxSize ); /** Retrieve goal configurations by using Platform Config Data @param[in, out] pDimmList Head of the list of all NVM DIMMs in the system @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RetrieveGoalConfigsFromPlatformConfigData( IN OUT LIST_ENTRY *pDimmList, IN BOOLEAN RestoreCorrupt ); /** Clear all internal goal configurations structures @param[in, out] pDimmList Head of the list of all NVM DIMMs in the system @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS ClearInternalGoalConfigsInfo( IN OUT LIST_ENTRY *pDimmList ); /** Check if specified persistent memory type contain valid value @param[in] PersistentMemType Persistent memory type @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Specified value is invalid **/ EFI_STATUS PersistentMemoryTypeValidation( IN UINT8 PersistentMemType ); /** Select one reserve Dimm from specified list of Dimms and remove it from the list @param[in, out] pDimms Array of pointers to DIMMs @param[in, out] pDimmsNum Number of pointers in pDimms @param[out] ppReserveDimm Selected Dimm from the list @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All Ok **/ EFI_STATUS SelectReserveDimm( IN OUT DIMM *pDimms[MAX_DIMMS], IN OUT UINT32 *pDimmsNum, OUT DIMM **ppReserveDimm ); /** Check if specified AppDirect Settings will conflict with existing AppDirect Interleaves @param[in] pDriverPreferences Driver preferences for AppDirect Provisioning @param[out] pConflict True, conflict exists with existing AppDirect Memory @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER parameter is NULL **/ EFI_STATUS AppDirectSettingsConflict( IN DRIVER_PREFERENCES *pDriverPreferences, OUT BOOLEAN *pConflict, OUT COMMAND_STATUS *pCommandStatus ); /** Check if specified AppDirect Settings contain valid values @param[in] pDriverPreferences Driver preferences for AppDirect Provisioning @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER parameter is NULL or specified values are not valid @retval EFI_ABORTED Unable to find required system tables **/ EFI_STATUS AppDirectSettingsValidation( IN DRIVER_PREFERENCES *pDriverPreferences ); /** Calculate actual volatile size with subtracted metadata size @param[in] RawDimmCapacity Raw capacity to calculate actual volatile size for @param[in] VolatileSizeRounded Rounded Volatile region size - without subtracted metadata size @param[out] pVolatileSizeActual Actual Volatile region size - with subtracted metadata size @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ EFI_STATUS CalculateActualVolatileSize( IN UINT64 RawDimmCapacity, IN UINT64 VolatileSizeRounded, OUT UINT64 *pVolatileSizeActual ); /** Calculate system wide capacity for a given percent @param[in] pDimms Array of pointers to DIMMs @param[in] pDimmsNum Number of pointers in pDimms @param[in] Percent Percent to calculate @param[out] pDimmsCapacity Output dimms capacity in bytes @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS Success **/ EFI_STATUS CalculateDimmCapacityFromPercent( IN DIMM *pDimms[MAX_DIMMS], IN UINT32 DimmsNum, IN UINT32 Percent, OUT UINT64 *pDimmsCapacity ); /** Verify DIMMs SKU support @param[in] ppDimms Array of dimms @param[in] DimmsNum Number of dimms @param[in, out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS No SKU violation @retval EFI_ABORTED SKU violation @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ EFI_STATUS VerifySKUSupportForCreateGoal( IN DIMM **ppDimms, IN UINT32 DimmsNum, IN OUT COMMAND_STATUS *pCommandStatus ); /** Calculate free Region capacity @param[in] pRegion Region that a free capacity will be calculated for @param[out] pFreeCapacity Output parameter for result @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS GetFreeRegionCapacity( IN NVM_IS *pRegion, OUT UINT64 *pFreeCapacity ); /** Map specified request to actual Region Goal templates. Resolve special "remaining" values. @param[in] pDimms Array of pointers to manageable DIMMs only @param[in] pDimmsNum Number of pointers in pDimms @param[out] DimmsSymmetrical Array of Dimms for symmetrical pool config @param[out] pDimmsSymmetricalNum Returned number of items in DimmsSymmetrical @param[out] DimmsAsymmetrical Array of Dimms for asymmetrical pool config @param[out] pDimmsAsymmetricalNum Returned number of items in DimmsAsymmetrical @param[in] PersistentMemType Persistent memory type @param[in] VolatileSize Volatile region size @param[in] ReservedPercent Amount of AppDirect memory to not map in percent @param[in] pMaxPMInterleaveSets Pointer to MaxPmInterleaveSets per Die & per Dcpmm @param[out] pVolatileSizeActual Actual Volatile region size @param[out] RegionGoalTemplates Array of pool goal templates @param[out] pRegionGoalTemplatesNum Number of items in RegionGoalTemplates @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS MapRequestToActualRegionGoalTemplates( IN DIMM *pDimms[MAX_DIMMS], IN UINT32 DimmsNum, OUT REGION_GOAL_DIMM DimmsSymmetrical[MAX_DIMMS], OUT UINT32 *pDimmsSymmetricalNum, OUT REGION_GOAL_DIMM DimmsAsymmetrical[MAX_DIMMS], OUT UINT32 *pDimmsAsymmetricalNum, IN UINT8 PersistentMemType, IN UINT64 VolatileSize, IN UINT32 ReservedPercent, IN MAX_PMINTERLEAVE_SETS *pMaxPMInterleaveSets, OUT UINT64 *pVolatileSizeActual OPTIONAL, OUT REGION_GOAL_TEMPLATE RegionGoalTemplates[MAX_IS_PER_DIMM], OUT UINT32 *pRegionGoalTemplatesNum, OUT COMMAND_STATUS *pCommandStatus ); /** Parse Interleave Information table and retrieve Region Goal (create a Region Goal if it doesn't exist yet) @param[in] pRegionGoals Array of all Region Goals in the system @param[in] RegionGoalsNum Number of pointers in pRegionGoals @param[in] pInterleaveInfo Interleave Information table retrieved from DIMM @param[in] PcdCinRev Revision of the PCD Config Input table @param[out] ppRegionGoal Output variable for Region Goal @param[out] pNew True if Region Goal new created, False if already exists @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RetrieveRegionGoalFromInterleaveInformationTable( IN REGION_GOAL *pRegionGoals[], IN UINT32 RegionGoalsNum, IN VOID *pInterleaveInfo, IN ACPI_REVISION PcdCinRev, OUT REGION_GOAL **ppRegionGoal, OUT BOOLEAN *pNew ); /** Map Regions Goal configs on specified DIMMs @param[in] DimmsSym Array of Dimms for symmetrical region config @param[in] DimmsSymNum Number of items in DimmsSym @param[in] DimmsAsym Array of Dimms for asymmetrical region config @param[in] DimmsAsymNum Number of items in DimmsAsym @param[in, out] pReserveDimm Dimm that its whole capacity will be set as persistent partition @param[in] ReserveDimmType Type of reserve dimm @param[in] VolatileSize Volatile region size in bytes @param[in] RegionGoalTemplates Array of template goal REGIONs @param[in] RegionGoalTemplatesNum Number of elements in RegionGoalTemplates @param[in] pDriverPreferences Driver preferences for interleave sets @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL or input DIMMs are different than affected DIMMs **/ EFI_STATUS MapRegionsGoal( IN REGION_GOAL_DIMM DimmsSym[MAX_DIMMS], IN UINT32 DimmsSymNum, IN REGION_GOAL_DIMM DimmsAsym[MAX_DIMMS], IN UINT32 DimmsAsymNum, IN OUT DIMM *pReserveDimm OPTIONAL, IN UINT8 ReserveDimmType OPTIONAL, IN UINT64 VolatileSize, IN REGION_GOAL_TEMPLATE RegionGoalTemplates[MAX_IS_PER_DIMM], IN UINT32 RegionGoalTemplatesNum, IN DRIVER_PREFERENCES *pDriverPreferences, OUT COMMAND_STATUS *pCommandStatus ); /** Delete regions goal configs from input DIMMs and (if force is true) all DIMMs related by regions @param[in, out] pDimms Array of pointers to DIMMs @param[in] DimmsNum Number of pointers in pDimms @param[in] Force Force to perform deleting regions goal configs on all affected DIMMs @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL or input DIMMs are different than affected DIMMs **/ EFI_STATUS DeleteRegionsGoalConfigs( IN OUT DIMM *pDimms[], IN UINT32 DimmsNum, OUT COMMAND_STATUS *pCommandStatus ); /** Verify that all the unconfigured DIMMs or all DIMMs on a given socket are configured at once to keep supported region configs. @param[in] pDimms List of DIMMs to configure @param[in] DimmsNum Number of DIMMs to configure @param[in] PersistentMemType Persistent memory type @param[in] VolatilePercent Volatile region size in percents @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER if one or more parameters are NULL @retval EFI_UNSUPPORTED A given config is unsupported **/ EFI_STATUS VerifyCreatingSupportedRegionConfigs( IN DIMM *pDimms[], IN UINT32 DimmsNum, IN UINT8 PersistentMemType, IN UINT32 VolatilePercent, OUT COMMAND_STATUS *pCommandStatus ); /** Reduce system wide socket mapped memory to align with the system memory mapped SKU limits per Socket. @param[in] Socket Socket Id for SKU limit calculations @param[in] pDimms Array of pointers to manageable DIMMs only @param[in] NumDimmsOnSocket Number of pointers in pDimms @param[in out] DimmsSymmetricalOnSocket Array of Dimms for symmetrical region config @param[in out] pDimmsSymmetricalNumOnSocket Returned number of items in DimmsSymmetrical @param[in out] DimmsAsymmetricalOnSocket Array of Dimms for asymmetrical region config @param[in out] pDimmsAsymmetricalNumOnSocket Returned number of items in DimmsAsymmetrical @param[in out] RegionGoalTemplates Array of region goal templates @param[in out] pRegionGoalTemplatesNum Number of items in RegionGoalTemplates @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_UNSUPPORTED Bad values retrieved from PCAT **/ EFI_STATUS ReduceCapacityForSocketSKU( IN UINT32 Socket, IN DIMM *pDimmsOnSocket[MAX_DIMMS], IN UINT32 NumDimmsOnSocket, IN OUT REGION_GOAL_DIMM DimmsSymmetricalOnSocket[MAX_DIMMS], IN OUT UINT32 *pDimmsSymmetricalNumOnSocket, IN OUT REGION_GOAL_DIMM DimmsAsymmetricalOnSocket[MAX_DIMMS], IN OUT UINT32 *pDimmsAsymmetricalNumOnSocket, IN OUT REGION_GOAL_TEMPLATE RegionGoalTemplates[MAX_IS_PER_DIMM], IN OUT UINT32 *pRegionGoalTemplatesNum, OUT COMMAND_STATUS *pCommandStatus ); /** Verify that specified Dimms don't affect other Dimms by current Regions or Regions goal configs @param[in, out] pDimms Array of pointers to DIMMs @param[in] DimmsNum Number of pointers in pDimms @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL or specified Dimms affect other Dimms **/ EFI_STATUS ValidateRegionsCorrelations( IN OUT DIMM *pDimms[], IN UINT32 DimmsNum, OUT COMMAND_STATUS *pCommandStatus ); /** Find related dimms based on region configs relations @param[in] pDimms Input array of pointers to dimms @param[in] DimmsNum Number of pointers in pDimms @param[out] pRelatedDimms Output array of pointers to dimms @param[out] pRelatedDimmsNum Output number of found dimms @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ EFI_STATUS FindRelatedDimmsByRegions( IN DIMM *pDimms[], IN UINT32 DimmsNum, OUT DIMM *pRelatedDimms[MAX_DIMMS], OUT UINT32 *pRelatedDimmsNum ); /** Find related dimms based on region goal configs relations @param[in] pDimms Input array of pointers to dimms @param[in] DimmsNum Number of pointers in pDimms @param[out] pRelatedDimms Output array of pointers to dimms @param[out] pRelatedDimmsNum Output number of found dimms @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ EFI_STATUS FindRelatedDimmsByRegionGoalConfigs( IN DIMM *pDimms[], IN UINT32 DimmsNum, OUT DIMM *pRelatedDimms[MAX_DIMMS], OUT UINT32 *pRelatedDimmsNum ); /** Find an unique list of goal regions based on input list of dimms @param[in] pDimms Input array of pointers to dimms @param[in] DimmsNum Number of pointers in pDimms @param[out] pRegionsGoal Output array of pointers to REGION_GOAL @param[out] pRegionsGoalNum Output number of unique goal regions @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_ABORTED More config goals found than can exist **/ EFI_STATUS FindUniqueRegionsGoal( IN DIMM *pDimms[], IN UINT32 DimmsNum, OUT REGION_GOAL *pRegionsGoal[MAX_IS_CONFIGS], OUT UINT32 *pRegionsGoalNum ); /** Cleans up pointers that are about to be freed so that double-free doesn't take place later on @param[in] pDimms Input array of pointers to dimms @param[in] DimmsNum Number of pointers in pDimms @param[in] pRegionsGoal to list of regions containing the candidate pointers @param[in] pRegionsGoalNum the number of region goal items **/ EFI_STATUS ClearRegionsGoal( IN DIMM *pDimms[], IN UINT32 DimmsNum, IN REGION_GOAL **pRegionsGoal, IN UINT32 pRegionsGoalNum ); /** Create REGION goal @param[in] pRegionGoalTemplate Pointer to REGION goal template @param[in] pDimms Array of pointers to DIMMs @param[in] DimmsNum Number of pointers in pDimms @param[in] InterleaveSetSize Interleave set size @param[in] pDriverPreferences Driver preferences for interleave sets Optional @param[in] SequenceIndex Variable to keep an order of REGIONs on DIMMs @param[in, out] pInterleaveSetIndex Unique index for interleave set @retval REGION_GOAL success @retval NULL one or more parameters are NULL or memory allocation failure **/ REGION_GOAL * CreateRegionGoal( IN REGION_GOAL_TEMPLATE *pRegionGoalTemplate, IN DIMM *pDimms[], IN UINT32 DimmsNum, IN UINT64 InterleaveSetSize, IN DRIVER_PREFERENCES *pDriverPreferences OPTIONAL, IN UINT16 SequenceIndex, IN OUT UINT16 *pInterleaveSetIndex ); /** Verify that all DIMMs with goal config are specified on a given socket at once to keep supported region configs. @param[in] pDimms List of DIMMs to configure @param[in] DimmsNum Number of DIMMs to configure @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER if one or more parameters are NULL @retval EFI_UNSUPPORTED A given config is unsupported **/ EFI_STATUS VerifyDeletingSupportedRegionConfigs( IN DIMM *pDimms[], IN UINT32 DimmsNum, OUT COMMAND_STATUS *pCommandStatus ); /** Find Region Goal Dimm in an array @param[in] pDimms Array that will be searched @param[in] DimmsNum Number of items in pDimms @param[in] pDimmToFind Dimm to find @retval Region Goal Dimm pointer - if found @retval NULL - if not found **/ REGION_GOAL_DIMM * FindRegionGoalDimm( IN REGION_GOAL_DIMM *pDimms, IN UINT32 DimmsNum, IN DIMM *pDimmToFind ); /** Calculate actual volatile size @param[in] RawDimmCapacity Raw capacity to calculate actual volatile size for @param[in] VolatileSizeRounded @param[out] pVolatileSizeActual Actual Volatile region size - actual volatile size with metadata @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER one or more parameters are NULL **/ EFI_STATUS CalculateActualVolatileSize( IN UINT64 RawDimmCapacity, IN UINT64 VolatileSizeRequested, OUT UINT64 *pVolatileSizeActual ); /** Check for new goal configs for the DIMM @param[in] pDIMM The current DIMM @param[out] pHasNewGoal TRUE if any of the dimms have a new goal, else FALSE @retval EFI_SUCCESS #retval EFI_INVALID_PARAMETER If IS is null **/ EFI_STATUS FindIfNewGoalOnDimm( IN DIMM *pDimm, OUT BOOLEAN *pHasNewGoal ); /** Check if security state of specified DIMM is locked @param[in] pDimm The current DIMM @param[out] pIsLocked TRUE if security state of specified dimm is locked @retval EFI_SUCCESS #retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES Allocation failed **/ EFI_STATUS IsDimmLocked( IN DIMM *pDimm, OUT BOOLEAN *pIsLocked ); /** Set Interleave Set state taking states' priority into account @param[in] CurrentState Current IS state @param[in] NewState IS state to be set @retval UINT8 New IS state **/ UINT8 SetISStateWithPriority( IN UINT8 CurrentState, IN UINT8 NewState ); /** Check for existing goal configs on a socket for which a new goal config has been requested @param[in] pDimms Array of pointers to DIMMs based on the goal config requested @param[in] pDimmsNum Number of pointers in pDimms @retval EFI_ABORTED one or more DIMMs on a socket already have goal configs @retval EFI_INVALID_PARAMETER pDimms or pDimmsNum is NULL **/ EFI_STATUS CheckForExistingGoalConfigPerSocket( IN DIMM *pDimms[MAX_DIMMS], IN UINT32 *pDimmsNum ); /** Examines the system topology for the system DDR capacity and compares it to the 2LM capacity to check for ratio violations @param[in] SocketId Socket Id, value 0xFFFF indicates include all socket values @param[in] pDimmsSym Array of Dimms for symmetrical region config @param[in] DimmsSymNum Number of items in DimmsSym @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER input parameter null **/ EFI_STATUS CheckNmFmLimits( IN UINT16 SocketId, IN REGION_GOAL_DIMM *pDimmsSym, IN UINT32 DimmsSymNum, OUT COMMAND_STATUS *pCommandStatus ); /** Checks if all DIMMs in the list are in configured state @param[IN] pDimmList Head of the Dimm list @param[IN] pDimmsUnConfigured Boolean flag to indicate if any PMem module is unconfigured @param[OUT] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER if input parameter null **/ EFI_STATUS CheckIfAllDimmsConfigured( IN LIST_ENTRY *pDimmList, OUT BOOLEAN *pDimmsUnConfigured, OUT COMMAND_STATUS *pCommandStatus OPTIONAL ); /** Calculate total far memory on PMem modules for existing goal configs @param[in] SocketId Socket Id, value 0xFFFF indicates include all socket values @param[in] pDimmsSym Array of Dimms for symmetrical region config @param[in] DimmsSymNum Number of items in DimmsSym @param[out] pTotalFarMemorySize Pointer to total far memory capacity @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER if input parameter null **/ EFI_STATUS CalculateFarMemorySizeForNewGoalConfigs( IN UINT16 SocketId, IN REGION_GOAL_DIMM *pDimmsSym, IN UINT32 DimmsSymNum, OUT UINT64 *pTotalFarMemorySize, OUT COMMAND_STATUS *pCommandStatus ); #endif ipmctl-03.00.00.0485/DcpmPkg/driver/NvmDimmDriver.c000066400000000000000000002053661440615110200213360ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include "NvmDimmDriver.h" #include "NvmDimmConfig.h" #include "NvmDimmDriverData.h" #include #include "NvmHealth.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifndef OS_BUILD #include #endif #if _BullseyeCoverage #ifndef OS_BUILD extern int cov_dumpData(void); #endif // !OS_BUILD #endif // _BullseyeCoverage #define FIRST_ERR(rc, newRc) { if (rc == EFI_SUCCESS) rc = newRc; } EFI_SYSTEM_TABLE *gSystemTable = NULL; EFI_GUID gNfitBindingProtocolGuid = { 0x97B4FA0C, 0x4D7E, 0xC2D0, { 0x67, 0x8E, 0xFB, 0x92, 0xE9, 0x6D, 0x2C, 0xC2 }}; EFI_GUID gNvmDimmNgnvmGuid = NVMDIMM_DRIVER_NGNVM_GUID; EFI_GUID gIntelDimmConfigVariableGuid = INTEL_DIMM_CONFIG_VARIABLE_GUID; EFI_GUID gIntelDimmPbrVariableGuid = INTEL_DIMM_PBR_VARIABLE_GUID; EFI_GUID gIntelDimmPbrTagIdVariableguid = INTEL_DIMM_PBR_TAGID_VARIABLE_GUID; #ifndef OS_BUILD EFI_GUID gDcpmmProtocolGuid = EFI_DCPMM_GUID; #endif // !OS_BUILD #ifdef OS_BUILD extern UINT8* gSmbiosTable; #endif // OS_BUILD /** Array of dimms UEFI-related data structures. **/ EFI_DIMMS_DATA gDimmsUefiData[MAX_DIMMS]; /** Driver Support EFI Version Protocol instance **/ GLOBAL_REMOVE_IF_UNREFERENCED EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gNvmDimmDriverDriverSupportedEfiVersion = { sizeof(EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL), EFI_2_31_SYSTEM_TABLE_REVISION }; /** Driver Binding Protocol instance **/ EFI_DRIVER_BINDING_PROTOCOL gNvmDimmDriverDriverBinding = { NvmDimmDriverDriverBindingSupported, NvmDimmDriverDriverBindingStart, NvmDimmDriverDriverBindingStop, NVMDIMM_VERSION, NULL, NULL }; /** Data structure **/ NVMDIMMDRIVER_DATA *gNvmDimmData = NULL; #ifndef OS_BUILD /** Track the NFIT protocol dimm handles that we've installed Device Path on **/ static EFI_HANDLE *gInstalledDevicePathProtocolHandles = NULL; static UINTN gNumberOfDevicePathProtocolsInstalled = 0; #endif /** Driver specific Vendor Device Path definition. **/ VENDOR_END_DEVICE_PATH gNvmDimmDriverDevicePath = { { { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { (UINT8) (sizeof(VENDOR_DEVICE_PATH)), (UINT8) ((sizeof(VENDOR_DEVICE_PATH)) >> 8) } }, NVMDIMM_DRIVER_DEVICE_PATH_GUID }, { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { (UINT8) (END_DEVICE_PATH_LENGTH), (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) } } }; /** DIMM device path. For each DIMM the NFIT device handle should be modified to distinct them. **/ ACPI_NVDIMM_DEVICE_PATH gNvmDimmDevicePathNode = { { ACPI_DEVICE_PATH, ACPI_NVDIMM_DP, { (UINT8) (sizeof(ACPI_NVDIMM_DEVICE_PATH)), (UINT8) ((sizeof(ACPI_NVDIMM_DEVICE_PATH)) >> 8) } }, 0 // Device handle to be set for each DIMM }; #ifndef OS_BUILD /** Function installs EfiDevicePathProtocolGuid on handles for NfitBinding protocols unless it is already tracking handles. @retval EFI_SUCCESS Protocol installed successfully @retval EFI_NOT_FOUND No Handles found Error return codes from LocateHandleBuffer and InstallMultipleProtocolInterfaces **/ EFI_STATUS InstallProtoEfiDevicePathProtocolToNfitBinding( ) { EFI_HANDLE *Buffer = NULL; UINTN BufSize = 0; UINTN Index = 0; EFI_STATUS ReturnCode = EFI_SUCCESS; /** If already installed just return **/ if ((gInstalledDevicePathProtocolHandles != NULL) && (gNumberOfDevicePathProtocolsInstalled > 0)) { NVDIMM_WARN("Install already recorded."); goto Finish; } /** check sanity and try to clean up if needed **/ if ((gInstalledDevicePathProtocolHandles != NULL) && (gNumberOfDevicePathProtocolsInstalled == 0)) { NVDIMM_ERR("gNumberOfDevicePathProtocolsInstalled is 0 and gInstalledDevicePathProtocolHandles is not NULL."); FREE_POOL_SAFE(gInstalledDevicePathProtocolHandles); gInstalledDevicePathProtocolHandles = NULL; } else if ((gInstalledDevicePathProtocolHandles == NULL) && (gNumberOfDevicePathProtocolsInstalled != 0)) { NVDIMM_ERR("gNumberOfDevicePathProtocolsInstalled is not 0 and gInstalledDevicePathProtocolHandles is NULL."); gNumberOfDevicePathProtocolsInstalled = 0; } /** Install device path protocol so reconnect will find handle **/ ReturnCode = gBS->LocateHandleBuffer(ByProtocol, &gNfitBindingProtocolGuid, NULL, &BufSize, &Buffer); if (ReturnCode == EFI_SUCCESS) { gNumberOfDevicePathProtocolsInstalled = BufSize; gInstalledDevicePathProtocolHandles = AllocateZeroPool(gNumberOfDevicePathProtocolsInstalled * sizeof(EFI_HANDLE)); if (gInstalledDevicePathProtocolHandles == NULL) { NVDIMM_WARN("Failed to allocate storage for gInstalledDevicePathProtocolHandles."); gNumberOfDevicePathProtocolsInstalled = 0; goto Finish; } for (Index = 0; Index < BufSize; Index++) { ReturnCode = gBS->InstallMultipleProtocolInterfaces( &Buffer[Index], &gEfiDevicePathProtocolGuid, &gNvmDimmDriverDevicePath, NULL); if (EFI_ERROR(ReturnCode)) { gInstalledDevicePathProtocolHandles[Index] = NULL; /** NULL so skip when freeing **/ NVDIMM_WARN("Failed to install the gEfiDevicePathProtocolGuid, error = 0x%llx.", ReturnCode); } else { gInstalledDevicePathProtocolHandles[Index] = Buffer[Index]; /** record the handle if successful **/ } } } else if (ReturnCode == EFI_NOT_FOUND) { /** nothing found so make sure we are not tracking any handles **/ gInstalledDevicePathProtocolHandles = NULL; gNumberOfDevicePathProtocolsInstalled = 0; } Finish: FREE_POOL_SAFE(Buffer); return ReturnCode; } #endif // UEFI /** Function tries to remove all of the block namespaces protocols, then it removes all of the enumerated namespaces from the LSA and also the regions. @retval EFI_SUCCESS the object were cleared successfully Error return codes from CleanBlockNamespaces function **/ EFI_STATUS CleanNamespacesAndISs( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; ReturnCode = CleanNamespaces(); if (EFI_ERROR(ReturnCode)) { goto Finish; } CleanNamespacesList(&gNvmDimmData->PMEMDev.Namespaces); LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); FreeLsaSafe(&pDimm->pLsa); } /** Remove Interleave Sets **/ CleanISLists(&gNvmDimmData->PMEMDev.Dimms, &gNvmDimmData->PMEMDev.ISs); gNvmDimmData->PMEMDev.RegionsAndNsInitialized = FALSE; CleanISLists(&gNvmDimmData->PMEMDev.Dimms, &gNvmDimmData->PMEMDev.ISsNfit); gNvmDimmData->PMEMDev.RegionsNfitInitialized = FALSE; Finish: return ReturnCode; } /** Function that allows to "refresh" the existing DIMMs. If a DIMM is inaccessible, all of the ISs and namespaces that it was a part of will be removed. @param[in] DoDriverCleanup, if the caller wants namespaces cleaned up including unloading protocols @retval EFI_SUCCESS if all DIMMs are working. @retval EFI_INVALID_PARAMETER if any of pointer parameters in NULL @retval EFI_ABORTED if at least one DIMM is not responding. @retval EFI_OUT_OF_RESOURCES if the memory allocation fails. @retval EFI_NO_RESPONSE FW busy for one or more dimms **/ EFI_STATUS ReenumerateNamespacesAndISs( IN BOOLEAN DoDriverCleanup ) { EFI_STATUS ReturnCode = EFI_SUCCESS; #ifndef OS_BUILD NVDIMM_ENTRY(); // TODO: Look into using ReinstallProtocolInterface() for the DoDriverCleanup // flow instead of manually for a simpler implementation. // Documentation is in the UEFI EDK2 Driver Writer's Guide. // Include runtime PCD corruption detection and device state // (i.e. goes down) in test cases. if (DoDriverCleanup == TRUE) { ReturnCode = CleanNamespacesAndISs(); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to clean namespaces and pools"); } } // Initialize Interleave Sets using PCD ReturnCode = InitializeInterleaveSets(FALSE); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to retrieve the REGION/IS list from PCD, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } // Initialize Interleave Sets using NFIT table ReturnCode = InitializeInterleaveSets(TRUE); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to retrieve the REGION/IS list from NFIT, error = " FORMAT_EFI_STATUS ".", ReturnCode); } /** Initialize Namespaces (read LSA, enumerate every namespace) **/ ReturnCode = InitializeNamespaces(); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to re-initialize namespaces, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } /** Install block and device path protocols on Namespaces **/ ReturnCode = InstallProtocolsOnNamespaces(); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to install protocols on namespaces, error = " FORMAT_EFI_STATUS ".", ReturnCode); } gNvmDimmData->PMEMDev.RegionsAndNsInitialized = TRUE; NVDIMM_EXIT_I64(ReturnCode); Finish: #endif return ReturnCode; } /** Unload the driver **/ EFI_STATUS EFIAPI NvmDimmDriverUnload( IN EFI_HANDLE ImageHandle ) { EFI_STATUS ReturnCode = EFI_SUCCESS; /** Uninitialize data associated with Playback and Record**/ PbrUninit(); #ifndef OS_BUILD EFI_STATUS TempReturnCode = EFI_SUCCESS; EFI_HANDLE *pHandleBuffer = NULL; UINTN HandleCount = 0; UINTN Index = 0; CONST BOOLEAN DriverAlreadyUnloaded = (gNvmDimmData == NULL); NVDIMM_ENTRY(); /** Uninstall EfiDevicePathProtocol on handles recorded earlier. **/ if ((gNumberOfDevicePathProtocolsInstalled > 0) && (gInstalledDevicePathProtocolHandles != NULL)) { for (Index = 0; Index < gNumberOfDevicePathProtocolsInstalled; Index++) { if (gInstalledDevicePathProtocolHandles[Index] != NULL) { ReturnCode = gBS->UninstallMultipleProtocolInterfaces( gInstalledDevicePathProtocolHandles[Index], &gEfiDevicePathProtocolGuid, &gNvmDimmDriverDevicePath, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Uninstall of EfiDevicePathProtocolGuid failed with code 0x%llx.", ReturnCode); } } else { NVDIMM_WARN("NULL handle encountered in gInstalledDevicePathProtocolHandles."); } } gNumberOfDevicePathProtocolsInstalled = 0; FREE_POOL_SAFE(gInstalledDevicePathProtocolHandles); gInstalledDevicePathProtocolHandles = NULL; } else if ((gNumberOfDevicePathProtocolsInstalled == 0) && (gInstalledDevicePathProtocolHandles != NULL)) { NVDIMM_WARN("gNumberOfDevicePathProtocolsInstalled is 0 and gInstalledDevicePathProtocolHandles is not NULL."); FREE_POOL_SAFE(gInstalledDevicePathProtocolHandles); gInstalledDevicePathProtocolHandles = NULL; } else if ((gNumberOfDevicePathProtocolsInstalled != 0) && (gInstalledDevicePathProtocolHandles == NULL)) { NVDIMM_WARN("gNumberOfDevicePathProtocolsInstalled is not 0 and gInstalledDevicePathProtocolHandles is NULL."); gNumberOfDevicePathProtocolsInstalled = 0; } /** Retrieve array of all handles in the handle database **/ ReturnCode = gBS->LocateHandleBuffer(AllHandles, NULL, NULL, &HandleCount, &pHandleBuffer); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to disconnect the driver from the handles. LocateHandleBuffer failed, error = " FORMAT_EFI_STATUS ".", ReturnCode); } else { /** Disconnect the current driver from any devices that might be still controlled **/ for (Index = 0; Index < HandleCount; Index++) { ReturnCode = EfiTestManagedDevice(pHandleBuffer[Index], gNvmDimmDriverDriverBinding.DriverBindingHandle, &gEfiDevicePathProtocolGuid); // If the handle is managed - disconnect it. if (!EFI_ERROR(ReturnCode)) { gBS->DisconnectController(pHandleBuffer[Index], ImageHandle, NULL); } } ReturnCode = EFI_SUCCESS; /** Free the array of handles **/ if (pHandleBuffer) { FreePool(pHandleBuffer); } } /** Uninstall protocols installed in the driver entry point **/ TempReturnCode = gBS->UninstallMultipleProtocolInterfaces(ImageHandle, &gEfiDriverBindingProtocolGuid, &gNvmDimmDriverDriverBinding, &gEfiComponentNameProtocolGuid, &gNvmDimmDriverComponentName, &gEfiDriverDiagnosticsProtocolGuid, &gNvmDimmDriverDriverDiagnostics, NULL); if (EFI_ERROR(TempReturnCode)) { FIRST_ERR(ReturnCode, TempReturnCode); NVDIMM_DBG("Failed to uninstall driver entry protocols, error = " FORMAT_EFI_STATUS ".", TempReturnCode); } /** remove these separately as a workaround for systems not supporting the correct version of UEFI **/ TempReturnCode = gBS->UninstallMultipleProtocolInterfaces(ImageHandle, &gEfiComponentName2ProtocolGuid, &gNvmDimmDriverComponentName2, &gEfiDriverDiagnostics2ProtocolGuid, &gNvmDimmDriverDriverDiagnostics2, NULL); if (EFI_ERROR(TempReturnCode)) { FIRST_ERR(ReturnCode, TempReturnCode); NVDIMM_DBG("Failed to uninstall driver entry protocols 2, error = " FORMAT_EFI_STATUS ".", TempReturnCode); } /** Uninstall Driver Supported EFI Version Protocol **/ TempReturnCode = gBS->UninstallMultipleProtocolInterfaces(ImageHandle, &gEfiDriverSupportedEfiVersionProtocolGuid, &gNvmDimmDriverDriverSupportedEfiVersion, NULL); if (EFI_ERROR(TempReturnCode)) { FIRST_ERR(ReturnCode, TempReturnCode); NVDIMM_DBG("Failed to uninstall the DriverSupportedEfiVersion protocol, error = " FORMAT_EFI_STATUS ".", TempReturnCode); } /** Uninstall Driver Health Protocol **/ TempReturnCode = gBS->UninstallMultipleProtocolInterfaces(ImageHandle, &gEfiDriverHealthProtocolGuid, &gNvmDimmDriverHealth, NULL); if (EFI_ERROR(TempReturnCode)) { FIRST_ERR(ReturnCode, TempReturnCode); NVDIMM_DBG("Failed to uninstall the DriverHealth protocol, error = 0x%llx.", TempReturnCode); } /** Uninstall Driver PBR Protocol **/ TempReturnCode = gBS->UninstallMultipleProtocolInterfaces(ImageHandle, &gNvmDimmPbrProtocolGuid, &gNvmDimmDriverNvmDimmPbr, NULL); if (EFI_ERROR(TempReturnCode)) { FIRST_ERR(ReturnCode, TempReturnCode); NVDIMM_DBG("Failed to uninstall the DriverPbr protocol, error = 0x%llx.", TempReturnCode); } if (EFI_ERROR(ReturnCode) && DriverAlreadyUnloaded) { NVDIMM_WARN("The driver was not properly initialized or was unloaded before, error = " FORMAT_EFI_STATUS ".", TempReturnCode); NVDIMM_DBG("Overriding the return code to SUCCESS"); ReturnCode = EFI_SUCCESS; } #endif /** clean up data struct **/ FREE_POOL_SAFE(gNvmDimmData); NVDIMM_DBG("Exiting DriverUnload, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #ifndef OS_BUILD /** This function reads the driver workarounds flags from the shell variable and sets the proper flags or values in the driver. This function exists only in the debug version of the driver. **/ #if defined(DYNAMIC_WA_ENABLE) /** Finds a driver handle for the given driver name keywords. The driver is identified by the ComponentNameProtocol->GetDriverName, so it is required that the requested driver implements this protocol. The input driver name should be a comma separated list of the words that exist in the name. The function assumes that the driver loaded in the system has one additional word in the name which is the driver version etc. The function is case sensitive but it does not care about the order of the words in the driver. The driver name in the ComponentNameProtocol should be delimited with spaces. Example usage: pDriverName = L"XXX,YYY,Driver" Will return a handle if in the system there is a driver loaded with names like: "XXX YYY 1.0.0.0 Driver", "YYY XXX 1.0.0.0 Driver", "XXX YYY Test123 Driver", "Driver XXX YYY Abc Driver", "YYY Driver Apa XXX", "Test123 XXX Driver YYY" In case of more than one match in the system, the first handle will be returned. **/ STATIC VOID FindDriverByComponentName( IN CONST CHAR16 *pDriverName, OUT EFI_HANDLE *pDriverHandle ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINTN HandleCount = 0; EFI_HANDLE *pHandleBuffer = NULL; UINT32 Index = 0; EFI_COMPONENT_NAME_PROTOCOL *pComponentName = NULL; CHAR16 *pCurrentDriverName = NULL; CHAR16 **ppSourceNameTokens = NULL; CHAR16 **ppCurrentNameTokens = NULL; UINT32 SourceNameTokenCount = 0; UINT32 CurrentNameTokenCount = 0; UINT32 TokensFound = 0; UINT32 Index2 = 0; UINT32 Index3 = 0; UINTN StringLength = 0; // The StrLen returns the length in UINTN NVDIMM_ENTRY(); if (pDriverHandle == NULL || pDriverHandle == NULL) { NVDIMM_DBG("Error in the provided parameters."); goto Finish; } *pDriverHandle = NULL; /** Find the driver handle by searching for the component name protocol **/ ReturnCode = gBS->LocateHandleBuffer(ByProtocol, &gEfiComponentNameProtocolGuid, NULL, &HandleCount, &pHandleBuffer); if (EFI_ERROR(ReturnCode) || HandleCount < 1) { NVDIMM_WARN("Failed to find any drivers with the component name installed, error = " FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } ppSourceNameTokens = StrSplit((CHAR16 *)pDriverName, L',', &SourceNameTokenCount); if (ppSourceNameTokens == NULL) { NVDIMM_DBG("Failed to parse the driver name."); goto Finish; } for (Index = 0; Index < HandleCount; Index++) { ReturnCode = gBS->OpenProtocol( pHandleBuffer[Index], &gEfiComponentNameProtocolGuid, (VOID **)&pComponentName, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to open the Component Name protocol, error = " FORMAT_EFI_STATUS "", ReturnCode); continue; } ReturnCode = pComponentName->GetDriverName(pComponentName, "eng", &pCurrentDriverName); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Could not get the driver name, error = " FORMAT_EFI_STATUS "", ReturnCode); continue; } ppCurrentNameTokens = StrSplit(pCurrentDriverName, L' ', &CurrentNameTokenCount); if (ppCurrentNameTokens == NULL) { NVDIMM_DBG("Could not parse the driver name."); continue; } if ((SourceNameTokenCount + 1) != CurrentNameTokenCount) { /** The token number should be the source +1 (the additional one is the version number that will be different, so we skip it in the search but must consider in the numbers **/ FreeStringArray(ppCurrentNameTokens, CurrentNameTokenCount); continue; // lets skip to the next driver } TokensFound = 0; for (Index2 = 0; Index2 < CurrentNameTokenCount; Index2++) { for (Index3 = 0; Index3 < SourceNameTokenCount; Index3++) { StringLength = StrLen(ppCurrentNameTokens[Index2]); if (StringLength > StrLen(ppSourceNameTokens[Index3])) { StringLength = StrLen(ppSourceNameTokens[Index3]); } if (CompareMem(ppCurrentNameTokens[Index2], ppSourceNameTokens[Index3], StringLength) == 0) { TokensFound++; } } } if (TokensFound == SourceNameTokenCount) { // Success, we assign the return value, clean the memory and leave the loop *pDriverHandle = pHandleBuffer[Index]; FreeStringArray(ppCurrentNameTokens, CurrentNameTokenCount); break; } FreeStringArray(ppCurrentNameTokens, CurrentNameTokenCount); } FreeStringArray(ppSourceNameTokens, SourceNameTokenCount); Finish: FREE_POOL_SAFE(pHandleBuffer); NVDIMM_EXIT(); } STATIC VOID InitWorkarounds( ) { CHAR16 *pShellVar = NULL; CHAR16 **ppShellVarSplit = NULL; UINT32 ShellTokensCount = 0; UINT32 Index = 0; pShellVar = GetEnvVariable(DYNAMIC_WA_ENV_VARIABLE_NAME); if (pShellVar != NULL) { if (NULL == (ppShellVarSplit = StrSplit(pShellVar, L',', &ShellTokensCount))) { return; } for (Index = 0; Index < ShellTokensCount; Index++) { if (CompareMem(ppShellVarSplit[Index], WA_FLAG_ALWAYS_LOAD, sizeof(WA_FLAG_ALWAYS_LOAD)) == 0) { Print(L"INFO: 'Always load' workaround enabled in the driver.\n"); gNvmDimmData->AlwaysLoadDriver = TRUE; continue; } if (CompareMem(ppShellVarSplit[Index], WA_FLAG_SIMICS, sizeof(WA_FLAG_SIMICS)) == 0) { Print(L"INFO: 'Simics' workaround enabled in the driver.\n"); gNvmDimmData->SimicsWorkarounds = TRUE; continue; } if (CompareMem(ppShellVarSplit[Index], WA_FLAG_UNLOAD_OTHER_DRIVERS, sizeof(WA_FLAG_UNLOAD_OTHER_DRIVERS)) == 0) { Print(L"INFO: 'Unload loaded drivers before load' workaround enabled in the driver.\n"); gNvmDimmData->UnloadExistingDrivers = TRUE; continue; } if (CompareMem(ppShellVarSplit[Index], WA_FLAG_IGNORE_UID_NUMS, sizeof(WA_FLAG_IGNORE_UID_NUMS)) == 0) { Print(L"INFO: Ignoring the same NVDIMM UID numbers workaround enabled in the driver.\n"); gNvmDimmData->IgnoreTheSameUIDNumbers = TRUE; continue; } if (CompareMem(ppShellVarSplit[Index], WA_FLAG_NO_PCD_INIT, sizeof(WA_FLAG_NO_PCD_INIT)) == 0) { Print(L"INFO: Disable PCD read during initialization workaround enabled in the driver.\n"); gNvmDimmData->PcdUsageDisabledOnInit = TRUE; continue; } } FreeStringArray(ppShellVarSplit, ShellTokensCount); } } /** Searches for currently loaded base and HII drivers If it finds them, they get unloaded. @param None @retval None **/ STATIC VOID UnloadDcpmmDriversIfAny( ) { CONST CHAR16 *pIpmctlDriverWordsToFind = PMEM_MODULE_NAME_SEARCH L",Driver"; CONST CHAR16 *pIpmctlHiiDriverWordsToFind = PMEM_MODULE_NAME_SEARCH L",HII,Driver"; EFI_HANDLE DriverHandle = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; FindDriverByComponentName(pIpmctlDriverWordsToFind, &DriverHandle); if (DriverHandle != NULL) { ReturnCode = gBS->UnloadImage(DriverHandle); if (!EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Successfully unloaded the base driver.\n"); } else { NVDIMM_WARN("Error while unloading the base driver, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); } } else { NVDIMM_DBG("Base driver not detected in the system.\n"); } FindDriverByComponentName(pIpmctlHiiDriverWordsToFind, &DriverHandle); if (DriverHandle != NULL) { ReturnCode = gBS->UnloadImage(DriverHandle); if (!EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Successfully unloaded the HII driver.\n"); } else { NVDIMM_WARN("Error while unloading the HII driver, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); } } else { NVDIMM_DBG("HII driver not detected in the system.\n"); } } #endif /** DYNAMIC_WA_ENABLE **/ /** RegisterDimmName Adds the DIMM name for the use of the ComponentName protocols. @param[in] DimmIndex, the index of the DIMM that the caller wants to register the name for. @retval EFI_SUCCESS if the name was added successfully Other return values from the function AddStringToUnicodeTable. **/ STATIC EFI_STATUS RegisterDimmName( IN UINT32 DimmIndex ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 *pDimmNameString = NULL; NVDIMM_ENTRY(); pDimmNameString = CatSPrint(NULL, PMEM_DIMM_NAME, gDimmsUefiData[DimmIndex].pDimm->DimmID); if (pDimmNameString != NULL) { ReturnCode = AddStringToUnicodeTable(pDimmNameString, &gDimmsUefiData[DimmIndex].pDimmName); } else { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_WARN("Failed to allocate memory for DIMM name.\n"); } FREE_POOL_SAFE(pDimmNameString); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif /**!OS_BUILD **/ /** Driver entry point **/ EFI_STATUS EFIAPI NvmDimmDriverDriverEntryPoint( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *pSystemTable ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_HANDLE ExistingDriver = NULL; #ifndef OS_BUILD SetSerialAttributes(); EFI_LOADED_IMAGE_PROTOCOL *pLoadedImage = NULL; #endif NVDIMM_ENTRY(); /** Set up playback/record data **/ ReturnCode = PbrInit(); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed to initialize PBR module, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); goto Finish; } /** This is the sample usage of the OutputCheckpoint function. The minor and major codes are custom. The BIOS scratchpad must be set to this value before the code gets there. Then the code will freeze until the value in the BIOS scratchpad will be changed. **/ //OutputCheckpoint(0x7d,0x00); /** Print runtime function address to ease calculation of GDB symbol loading offset. **/ NVDIMM_DBG_CLEAN("NvmDimmDriverDriverEntryPoint=0x%x\n", (UINT64)&NvmDimmDriverDriverEntryPoint); gSystemTable = pSystemTable; /** We need to set the Driver Binding Image handle. The other handle is set by EfiLibInstallAllDriverProtocols2 function. **/ gNvmDimmDriverDriverBinding.ImageHandle = ImageHandle; /** Set up the driver data **/ gNvmDimmData = AllocateZeroPool(sizeof(NVMDIMMDRIVER_DATA)); if (gNvmDimmData == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Unable to allocate NvmDimmData, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); goto Finish; } gNvmDimmData->DriverHandle = ImageHandle; gNvmDimmData->Alignments.RegionPartitionAlignment = SIZE_1GB; gNvmDimmData->Alignments.RegionVolatileAlignment = REGION_VOLATILE_SIZE_ALIGNMENT_B; gNvmDimmData->Alignments.RegionPersistentAlignment = REGION_PERSISTENT_SIZE_ALIGNMENT_B; gNvmDimmData->Alignments.PmNamespaceMinSize = PM_NAMESPACE_MIN_SIZE; #if defined(DYNAMIC_WA_ENABLE) #ifndef OS_BUILD InitWorkarounds(); if (gNvmDimmData->UnloadExistingDrivers) { // Search for the driver being loaded. If it is loaded but not started, the only protocol that can find UnloadDcpmmDriversIfAny(); } #endif #endif ReturnCode = GetDriverHandle(&gNvmDimmConfigProtocolGuid, &ExistingDriver); if (!EFI_ERROR(ReturnCode)) { #ifndef OS_BUILD NVDIMM_WARN("NvmDimmConfigProtocol already installed, please unload the driver before loading again."); ReturnCode = EFI_ALREADY_STARTED; goto Finish; #endif } /** Initialize list heads contained in MEMDev. **/ PMEMDEV_INITIALIZER(&gNvmDimmData->PMEMDev); #ifndef OS_BUILD /** Install UEFI Driver Model protocol(s). **/ ReturnCode = EfiLibInstallAllDriverProtocols2( ImageHandle, pSystemTable, &gNvmDimmDriverDriverBinding, ImageHandle, &gNvmDimmDriverComponentName, &gNvmDimmDriverComponentName2, NULL, NULL, &gNvmDimmDriverDriverDiagnostics, &gNvmDimmDriverDriverDiagnostics2); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to install the driver protocols, error = 0x%llx.", ReturnCode); goto Finish; } /** Locate DCPMM - BIOS protocol for writing to mailboxes in UEFI **/ #ifndef OS_BUILD ReturnCode = gBS->LocateProtocol(&gDcpmmProtocolGuid, NULL, (VOID **)&gNvmDimmData->pDcpmmProtocol); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_NOT_FOUND) { NVDIMM_WARN("Dcpmm protocol not found"); } else { NVDIMM_WARN("Communication with the device driver failed (dcpmm protocol)"); } goto Finish; } // Make sure the protocol version is supported. if (gNvmDimmData->pDcpmmProtocol->ProtocolVersion != DCPMM_PROTOCOL_VER_2) { NVDIMM_ERR("Unexpected DCPMM protocol version. Expected %d got %d", DCPMM_PROTOCOL_VER_2,gNvmDimmData->pDcpmmProtocol->ProtocolVersion); ReturnCode = EFI_UNSUPPORTED; goto Finish; } #endif // !OS_BUILD /** Install Driver Supported EFI Version Protocol onto ImageHandle. **/ ReturnCode = gBS->InstallMultipleProtocolInterfaces(&ImageHandle, &gEfiDriverSupportedEfiVersionProtocolGuid, &gNvmDimmDriverDriverSupportedEfiVersion, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to install the EfiVersionProtocol, error = 0x%llx.", ReturnCode); goto Finish; } /** Install Driver Health Protocol onto ImageHandle **/ ReturnCode = gBS->InstallMultipleProtocolInterfaces( &ImageHandle, &gEfiDriverHealthProtocolGuid, &gNvmDimmDriverHealth, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to install the EfiDriverHealthProtocol, error = 0x%llx.", ReturnCode); goto Finish; } /** Install Pbr Protocol onto ImageHandle **/ ReturnCode = gBS->InstallMultipleProtocolInterfaces( &ImageHandle, &gNvmDimmPbrProtocolGuid, &gNvmDimmDriverNvmDimmPbr, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to install the EfiDriverPbrProtocol, error = 0x%llx.", ReturnCode); goto Finish; } InstallProtoEfiDevicePathProtocolToNfitBinding(); #endif // UEFI Finish: #ifndef OS_BUILD /** Install the unload function on the loaded image protocol **/ if (!EFI_ERROR(ReturnCode)) { ReturnCode = gBS->OpenProtocol(ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&pLoadedImage, ImageHandle, ImageHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (!EFI_ERROR(ReturnCode)) { pLoadedImage->Unload = NvmDimmDriverUnload; } } else { /** clean - call unload manually if we failed to initialize the driver **/ NvmDimmDriverUnload(ImageHandle); } #endif NVDIMM_DBG("Exiting DriverEntryPoint, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** This function creates a handle with the NFIT Binding Protocol and then calls our binding start function on it. It allows us to always load the driver, even if this handle is not properly populated in the system. **/ #if !defined(MDEPKG_NDEBUG) #ifndef OS_BUILD STATIC #endif // UEFI EFI_STATUS ForceStartTheDriver( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_HANDLE FakeBindHandle = NULL; /** Create a new handle to bind to, so that we are not depended on any existing device handle. **/ ReturnCode = gBS->InstallMultipleProtocolInterfaces( &FakeBindHandle, &gNfitBindingProtocolGuid, NULL, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Could not install the emulated device for binding: " FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } else { ReturnCode = NvmDimmDriverDriverBindingStart(&gNvmDimmDriverDriverBinding, FakeBindHandle, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Could not start the driver on the emulated handle: " FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } } Finish: // Set this flag, even if we fail to load -> we just won't load if we failed at the first time gNvmDimmData->HandleCreated = TRUE; return ReturnCode; } #endif /** Tests to see if this driver supports a given controller. If a child device is provided, it further tests to see if this driver supports creating a handle for the specified child device. **/ EFI_STATUS EFIAPI NvmDimmDriverDriverBindingSupported( IN EFI_DRIVER_BINDING_PROTOCOL *pThis, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *pRemainingDevicePath OPTIONAL ) { EFI_STATUS ReturnCode = EFI_UNSUPPORTED; #ifndef OS_BUILD VOID *pDummy = NULL; //NVDIMM_ENTRY(); //disabled because of flooding #if !defined(MDEPKG_NDEBUG) if (gNvmDimmData->AlwaysLoadDriver) { if (!gNvmDimmData->HandleCreated) { ReturnCode = ForceStartTheDriver(); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to force start the driver: " FORMAT_EFI_STATUS "", ReturnCode); } } else { // If we made our own handle, we are already bound to it - we should not use it at all goto Finish; } } #endif /** Try to open NFIT protocol on this controller handle. **/ ReturnCode = gBS->OpenProtocol(ControllerHandle, &gNfitBindingProtocolGuid, (VOID **)&pDummy, pThis->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (!EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Detected the Intel NVM Dimm device"); /** If the device path protocol has been already opened - we had already started. **/ ReturnCode = EfiTestManagedDevice(ControllerHandle, gNvmDimmDriverDriverBinding.DriverBindingHandle, &gNfitBindingProtocolGuid); if (!EFI_ERROR(ReturnCode)) { ReturnCode = EFI_ALREADY_STARTED; goto Finish; } else { ReturnCode = EFI_SUCCESS; } } else { /** One way or another - if we had an error, we need to return EFI_UNSUPPORTED **/ ReturnCode = EFI_UNSUPPORTED; } Finish: #endif //NVDIMM_EXIT_I64(ReturnCode); //disabled because of flooding return ReturnCode; }; /** AddStringToUnicodeTable Adds the provided string in the "en/eng" language to the provided UNICODE STRING TABLE. @param[in] pStringToAdd pointer to the Unicode string to be added. @param[in,out] ppTableToAddTo pointer to a pointer to the UNICODE STRING TABLE that the string needs to be added. @retval EFI_SUCCESS - everything went fine. @retval EFI_INVALID_PARAMETER - at least one of the input parameters equals NULL. Other return values from the AddUnicodeString2 function. **/ EFI_STATUS AddStringToUnicodeTable( IN CHAR16 *pStringToAdd, IN OUT EFI_UNICODE_STRING_TABLE **ppTableToAddTo ) { EFI_STATUS ReturnCode = EFI_SUCCESS; #ifndef OS_BUILD if (pStringToAdd == NULL || ppTableToAddTo == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = AddUnicodeString2( "eng", gNvmDimmDriverComponentName.SupportedLanguages, ppTableToAddTo, pStringToAdd, TRUE ); if (EFI_ERROR(ReturnCode)) { /** This is not a critical error - the driver will still work properly. We report a warning. **/ NVDIMM_DBG("Failed to register Unicode name, error = " FORMAT_EFI_STATUS "\n", ReturnCode); } ReturnCode = AddUnicodeString2( "en", gNvmDimmDriverComponentName2.SupportedLanguages, ppTableToAddTo, pStringToAdd, FALSE ); if (EFI_ERROR(ReturnCode)) { /** This is not a critical error - the driver will still work properly. We report a warning. **/ NVDIMM_DBG("Failed to register DIMM name, error = " FORMAT_EFI_STATUS "\n", ReturnCode); } Finish: #endif return ReturnCode; } /** This function makes calls to the dimms required to initialize the driver. @retval EFI_SUCCESS if no errors. @retval EFI_xxxx depending on error encountered. **/ EFI_STATUS InitializeDimms() { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS ReturnCodeNonBlocking = EFI_SUCCESS; UINT32 Index = 0; DIMM *pDimm = NULL; DIMM *pDimm2 = NULL; LIST_ENTRY *pDimmNode = NULL; LIST_ENTRY *pDimmNode2 = NULL; CHAR16 Dimm1Uid[MAX_DIMM_UID_LENGTH]; CHAR16 Dimm2Uid[MAX_DIMM_UID_LENGTH]; NVDIMM_ENTRY(); #ifndef OS_BUILD BOOLEAN PcdUsage = TRUE; EFI_DEVICE_PATH_PROTOCOL *pTempDevicePathInterface = NULL; #endif /** enumerate DCPMMs **/ ReturnCodeNonBlocking = FillDimmList(); if (EFI_ERROR(ReturnCodeNonBlocking)) { NVDIMM_WARN("Failed to initialize Dimms, error = " FORMAT_EFI_STATUS ".", ReturnCodeNonBlocking); } #ifndef OS_BUILD ReturnCodeNonBlocking = SmbusInit(); if (EFI_ERROR(ReturnCodeNonBlocking)) { NVDIMM_WARN("Failed on Smbus init, error = " FORMAT_EFI_STATUS ".", ReturnCodeNonBlocking); } #endif //!OS_BUILD /** Verify that all manageable NVM-DIMMs have unique identifier. Otherwise, print a critical error and break further initialization. **/ LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (!IsDimmManageable(pDimm)) { continue; } LIST_FOR_EACH(pDimmNode2, &gNvmDimmData->PMEMDev.Dimms) { pDimm2 = DIMM_FROM_NODE(pDimmNode2); if (IsDimmManageable(pDimm2)) { ZeroMem(Dimm1Uid, sizeof(Dimm1Uid)); ZeroMem(Dimm2Uid, sizeof(Dimm2Uid)); GetDimmUid(pDimm, Dimm1Uid, MAX_DIMM_UID_LENGTH); GetDimmUid(pDimm2, Dimm2Uid, MAX_DIMM_UID_LENGTH); if (pDimm != pDimm2 && (StrICmp(Dimm1Uid,Dimm2Uid) == 0)) { NVDIMM_ERR("NVM-DIMMs with the same NVDIMM UID have been detected."); #if defined(DYNAMIC_WA_ENABLE) if (gNvmDimmData->IgnoreTheSameUIDNumbers) { NVDIMM_DBG("Ignoring same NVDIMM UIDs among dimms"); } else { #endif ReturnCode = EFI_DEVICE_ERROR; goto Finish; #if defined(DYNAMIC_WA_ENABLE) } #endif } } } } #ifndef OS_BUILD #if defined(DYNAMIC_WA_ENABLE) PcdUsage = !gNvmDimmData->PcdUsageDisabledOnInit; #endif if (PcdUsage) { /** We try to initialize all Regions, but if something goes wrong with a specific Region, then we just don't create the Region or add a proper error state to it. So even then we continue the driver initialization. **/ // Initialize Interleave Sets using PCD ReturnCode = InitializeInterleaveSets(FALSE); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to retrieve the REGION/IS list from PCD, error = " FORMAT_EFI_STATUS ".", ReturnCode); } // Initialize Interleave Sets using NFIT table ReturnCode = InitializeInterleaveSets(TRUE); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to retrieve the REGION/IS list from NFIT, error = " FORMAT_EFI_STATUS ".", ReturnCode); } /** Initialize Namespaces **/ ReturnCode = InitializeNamespaces(); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to initialize Namespaces, error = " FORMAT_EFI_STATUS ".", ReturnCode); } } #endif // !OS_BUILD Index = 0; for (pDimmNode = GetFirstNode(&gNvmDimmData->PMEMDev.Dimms); !IsNull(&gNvmDimmData->PMEMDev.Dimms, pDimmNode); pDimmNode = GetNextNode(&gNvmDimmData->PMEMDev.Dimms, pDimmNode)) { pDimm = DIMM_FROM_NODE(pDimmNode); /** Technically this should be NULL as it is in a global array, but we NULL it here just to be sure that the handle will be created in the first call to the "InstallMultipleProtocolInterfaces" **/ gDimmsUefiData[Index].DeviceHandle = NULL; gDimmsUefiData[Index].pDevicePath = NULL; gDimmsUefiData[Index].pDimm = pDimm; gNvmDimmDevicePathNode.NFITDeviceHandle = pDimm->DeviceHandle.AsUint32; gDimmsUefiData[Index].pDevicePath = AppendDevicePathNode( gNvmDimmData->pControllerDevicePathInstance, (CONST EFI_DEVICE_PATH_PROTOCOL *) &gNvmDimmDevicePathNode); if (gDimmsUefiData[Index].pDevicePath == NULL) { NVDIMM_WARN("Failed to create DIMM logic unit device path, not enough resources."); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } #ifndef OS_BUILD ReturnCode = gBS->InstallMultipleProtocolInterfaces( &gDimmsUefiData[Index].DeviceHandle, &gEfiDevicePathProtocolGuid, gDimmsUefiData[Index].pDevicePath, NULL); NVDIMM_DBG("gBS->InstallMultipleProtocolInterfaces(...)[%d] ReturnCode = %d", Index, ReturnCode); NVDIMM_DBG("gDimmsUefiData[%d].DeviceHandle was set to %d", Index, gDimmsUefiData[Index].DeviceHandle); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to install Device Path protocol, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } ReturnCode = gBS->OpenProtocol( gNvmDimmData->ControllerHandle, &gEfiDevicePathProtocolGuid, (VOID **)&pTempDevicePathInterface, gNvmDimmDriverDriverBinding.DriverBindingHandle, gDimmsUefiData[Index].DeviceHandle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ); if (EFI_ERROR(ReturnCode)) { /** This is not a critical error - the driver will still work properly. We report a warning. **/ NVDIMM_WARN("Failed to open parent Device Path protocol, error = " FORMAT_EFI_STATUS ".", ReturnCode); } /** Register Dimm name **/ ReturnCode = RegisterDimmName(Index); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to add the DIMM name, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } /** Check if we support this DIMMs Firmware API. **/ if (!IsDimmManageable(pDimm)) { /** Install only firmware update, security and label protocols on a manageable DIMM. **/ Index++; continue; } /** This assignment copies the global instance content to each local protocol instance. **/ gDimmsUefiData[Index].FirmwareManagementInstance = gNvmDimmFirmwareManagementProtocol; ReturnCode = gBS->InstallMultipleProtocolInterfaces( &gDimmsUefiData[Index].DeviceHandle, &gNvmDimmFirmwareManagementProtocolGuid, &gDimmsUefiData[Index].FirmwareManagementInstance, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to install Firmware Management Protocol, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } gDimmsUefiData[Index].StorageSecurityCommandInstance = gNvmDimmDriverStorageSecurityCommand; ReturnCode = gBS->InstallMultipleProtocolInterfaces( &gDimmsUefiData[Index].DeviceHandle, &gEfiStorageSecurityCommandProtocolGuid, &gDimmsUefiData[Index].StorageSecurityCommandInstance, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to install Storage Security Command protocol, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } gDimmsUefiData[Index].NvdimmLabelProtocolInstance = gNvdimmLabelProtocol; ReturnCode = gBS->InstallMultipleProtocolInterfaces( &gDimmsUefiData[Index].DeviceHandle, &gEfiNvdimmLabelProtocolGuid, &gDimmsUefiData[Index].NvdimmLabelProtocolInstance, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to install NVDIMM label protocol, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } Index++; #endif // !OS_BUILD } #ifndef OS_BUILD ReturnCode = InstallProtocolsOnNamespaces(); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to install Block Namespace devices, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } #endif // !OS_BUILD Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #ifdef OS_BUILD /** Starts a device controller or a bus controller. **/ EFI_STATUS EFIAPI NvmDimmDriverDriverBindingStart( IN EFI_DRIVER_BINDING_PROTOCOL *pThis, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *pRemainingDevicePath OPTIONAL ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Index = 0; DIMM *pDimm = NULL; DIMM *pDimm2 = NULL; LIST_ENTRY *pDimmNode = NULL; LIST_ENTRY *pDimmNode2 = NULL; CHAR16 Dimm1Uid[MAX_DIMM_UID_LENGTH]; CHAR16 Dimm2Uid[MAX_DIMM_UID_LENGTH]; NVDIMM_ENTRY(); /** Remember the Controller handle that we were started with. **/ gNvmDimmData->ControllerHandle = ControllerHandle; gNvmDimmData->NvmDimmConfig = gNvmDimmDriverNvmDimmConfig; /** load the ACPI Tables (NFIT, PCAT, PMTT) **/ ReturnCode = initAcpiTables(); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to initialize the ACPI tables, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } /** check the NFIT SPA range map against the memory map **/ ReturnCode = CheckMemoryMap(); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed while checking memory map, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } /** enumerate DCPMMs **/ ReturnCode = FillDimmList(); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to initialize Dimms, error = " FORMAT_EFI_STATUS ".", ReturnCode); } /** Verify that all manageable NVM-DIMMs have unique identifier. Otherwise, print a critical error and break further initialization. **/ LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (!IsDimmManageable(pDimm)) { continue; } LIST_FOR_EACH(pDimmNode2, &gNvmDimmData->PMEMDev.Dimms) { pDimm2 = DIMM_FROM_NODE(pDimmNode2); if (IsDimmManageable(pDimm2)) { ZeroMem(Dimm1Uid, sizeof(Dimm1Uid)); ZeroMem(Dimm2Uid, sizeof(Dimm2Uid)); GetDimmUid(pDimm, Dimm1Uid, MAX_DIMM_UID_LENGTH); GetDimmUid(pDimm2, Dimm2Uid, MAX_DIMM_UID_LENGTH); if (pDimm != pDimm2 && (StrICmp(Dimm1Uid, Dimm2Uid) == 0)) { NVDIMM_ERR("NVM-DIMMs with the same NVDIMM UID have been detected."); #if defined(DYNAMIC_WA_ENABLE) if (gNvmDimmData->IgnoreTheSameUIDNumbers) { NVDIMM_DBG("Ignoring same NVDIMM UIDs among dimms"); } else { #endif ReturnCode = EFI_DEVICE_ERROR; goto Finish; #if defined(DYNAMIC_WA_ENABLE) } #endif } } } } Index = 0; for (pDimmNode = GetFirstNode(&gNvmDimmData->PMEMDev.Dimms); !IsNull(&gNvmDimmData->PMEMDev.Dimms, pDimmNode); pDimmNode = GetNextNode(&gNvmDimmData->PMEMDev.Dimms, pDimmNode)) { pDimm = DIMM_FROM_NODE(pDimmNode); /** Technically this should be NULL as it is in a global array, but we NULL it here just to be sure that the handle will be created in the first call to the "InstallMultipleProtocolInterfaces" **/ gDimmsUefiData[Index].DeviceHandle = NULL; gDimmsUefiData[Index].pDevicePath = NULL; gDimmsUefiData[Index].pDimm = pDimm; gNvmDimmDevicePathNode.NFITDeviceHandle = pDimm->DeviceHandle.AsUint32; gDimmsUefiData[Index].pDevicePath = AppendDevicePathNode( gNvmDimmData->pControllerDevicePathInstance, (CONST EFI_DEVICE_PATH_PROTOCOL *) &gNvmDimmDevicePathNode); if (gDimmsUefiData[Index].pDevicePath == NULL) { NVDIMM_WARN("Failed to create DIMM logic unit device path, not enough resources."); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } Index++; } Finish: if (EFI_ERROR(ReturnCode)) { // UEFI doesn't call DriverBindingStop if we return an error on // DriverBindingStart (this function). So to tear down properly // let's just call DriverBindingStop(). // NOTE: Not sure if this is needed for OS, but it seems reasonable to do. NVDIMM_DBG("Error during DriverBindingStart(). Calling DriverBindingStop() to teardown"); CHECK_RESULT_CONTINUE(NvmDimmDriverDriverBindingStop(pThis, ControllerHandle, 0, NULL)); } NVDIMM_DBG("Exiting DriverBindingStart, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #else // !OS_BUILD /** Starts a device controller or a bus controller. **/ EFI_STATUS EFIAPI NvmDimmDriverDriverBindingStart( IN EFI_DRIVER_BINDING_PROTOCOL *pThis, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *pRemainingDevicePath OPTIONAL ) { EFI_STATUS ReturnCode = EFI_SUCCESS; VOID *pDummy = 0; INTEL_DIMM_CONFIG *pIntelDIMMConfig = NULL; UINT32 TagId = 0; PbrContext *ctx = PBR_CTX(); UINT32 NextId = 0; NVDIMM_ENTRY(); if (PBR_RECORD_MODE == PBR_GET_MODE(ctx)) { //set a tag id to mark the start of driver initialization PbrSetTag(PBR_DCPMM_CLI_SIG, L"driver: initialization", L"0", &TagId); } else if (PBR_PLAYBACK_MODE == PBR_GET_MODE(ctx)) { //The id is saved to a non-persistent volatile store, and is incremented //after each CLI cmd and drive load invocation. Given we have the tagid that should //be executed next, explicitly reset the pbr session to that id before //running the cmd. PbrDcpmmDeserializeTagId(&NextId, 0); PbrResetSession(NextId); PbrDcpmmSerializeTagId(NextId + 1); } /** Remember the Controller handle that we were started with. **/ gNvmDimmData->ControllerHandle = ControllerHandle; /** Install EFI_DCPMM_CONFIG2_PROTOCOL on the driver handle **/ ReturnCode = gBS->InstallMultipleProtocolInterfaces(&gNvmDimmData->DriverHandle, &gNvmDimmConfigProtocolGuid, &gNvmDimmDriverNvmDimmConfig, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to install the NvmDimmConfigProtocol, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } gNvmDimmData->NvmDimmConfig = gNvmDimmDriverNvmDimmConfig; /** Open the device path protocol to prepare for appending DIMM nodes. **/ ReturnCode = gBS->OpenProtocol( ControllerHandle, &gEfiDevicePathProtocolGuid, (VOID **)&gNvmDimmData->pControllerDevicePathInstance, pThis->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER ); /** If failed to open then try to install and retry open. **/ if (ReturnCode != EFI_SUCCESS) { InstallProtoEfiDevicePathProtocolToNfitBinding(); ReturnCode = gBS->OpenProtocol( ControllerHandle, &gEfiDevicePathProtocolGuid, (VOID **)&gNvmDimmData->pControllerDevicePathInstance, pThis->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER ); } if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to open Device Path protocol, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto FinishSkipClose; } ReturnCode = gBS->OpenProtocol( ControllerHandle, &gNfitBindingProtocolGuid, &pDummy, pThis->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to open NFIT Binding protocol, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto FinishSkipClose; } gNvmDimmData->HiiHandle = HiiAddPackages(&gNvmDimmNgnvmGuid, gNvmDimmData->DriverHandle, IntelOptanePMemDriverStrings, NULL); if (gNvmDimmData->HiiHandle == NULL) { NVDIMM_WARN("Unable to add string package to Hii"); goto Finish; } /** load the ACPI Tables (NFIT, PCAT and PMTT) **/ ReturnCode = initAcpiTables(); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to initialize the ACPI tables, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } /** check the NFIT SPA range map against the memory map **/ ReturnCode = CheckMemoryMap(); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed while checking memory map, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } /** load the ARS list **/ ReturnCode = LoadArsList(); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to load the ARS list, error = " FORMAT_EFI_STATUS ".", ReturnCode); } // Ignore return code as we don't want to block the ability to work // with functional dimms ReturnCode = InitializeSmbusAccess(); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to start SMBUS access, error = 0x%llx.\nContinuing...", ReturnCode); } /** Initialize DIMMs, ISets and namespaces **/ ReturnCode = InitializeDimms(); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed while checking memory map, error = " FORMAT_EFI_STATUS ".", ReturnCode); goto Finish; } /** Check Intel DIMM Config EFI variables whether to perform automatic provisioning **/ ReturnCode = RetrieveIntelDIMMConfig(&pIntelDIMMConfig); if (EFI_ERROR(ReturnCode)) { // Does not affect BindingStart status, just skip auto provision ReturnCode = EFI_SUCCESS; goto Finish; } if (pIntelDIMMConfig->ProvisionCapacityMode == PROVISION_CAPACITY_MODE_AUTO) { NVDIMM_DBG("Entering automatic capacity provisioning flow."); AutomaticProvisionCapacity(pIntelDIMMConfig); } if (pIntelDIMMConfig->ProvisionNamespaceMode == PROVISION_CAPACITY_MODE_AUTO) { NVDIMM_DBG("Entering automatic namespace provisioning flow."); AutomaticProvisionNamespace(pIntelDIMMConfig); } Finish: if (EFI_ERROR(ReturnCode)) { // UEFI doesn't call DriverBindingStop if we return an error on // DriverBindingStart (this function). So to tear down properly // let's just call DriverBindingStop(). NVDIMM_DBG("Error during DriverBindingStart(). Calling DriverBindingStop() to teardown"); CHECK_RESULT_CONTINUE(NvmDimmDriverDriverBindingStop(pThis, ControllerHandle, 0, NULL)); } FREE_POOL_SAFE(pIntelDIMMConfig); if (EFI_ERROR(ReturnCode)) { gBS->CloseProtocol( ControllerHandle, &gEfiDevicePathProtocolGuid, pThis->DriverBindingHandle, ControllerHandle ); if (gNvmDimmData->HiiHandle != NULL) { HiiRemovePackages(gNvmDimmData->HiiHandle); } } FinishSkipClose: NVDIMM_DBG("Exiting DriverBindingStart, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif // !OS_BUILD /** Stops a device controller or a bus controller. **/ EFI_STATUS EFIAPI NvmDimmDriverDriverBindingStop( IN EFI_DRIVER_BINDING_PROTOCOL *pThis, IN EFI_HANDLE ControllerHandle, IN UINTN NumberOfChildren, IN EFI_HANDLE *pChildHandleBuffer OPTIONAL ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TempReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; UINT32 Index = 0; NVDIMM_ENTRY(); if (gNvmDimmData == NULL) { NVDIMM_WARN("Driver data structure not initialized!\n"); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } if (gNvmDimmData->ControllerHandle == NULL) { NVDIMM_WARN("Driver already stopped.\n"); ReturnCode = EFI_SUCCESS; goto Finish; } if (ControllerHandle == NULL) { NVDIMM_WARN("The stop controller handle is NULL.\n"); ReturnCode = EFI_UNSUPPORTED; goto Finish; } if (ControllerHandle != gNvmDimmData->ControllerHandle) { NVDIMM_WARN("The stop controller handle differs from the Start controller handle.\n"); ReturnCode = EFI_UNSUPPORTED; goto Finish; } ReturnCode = CleanNamespacesAndISs(); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to clean the Namespaces Block Devices, error = " FORMAT_EFI_STATUS "\n.", ReturnCode); } Index = 0; for (pDimmNode = GetFirstNode(&gNvmDimmData->PMEMDev.Dimms); !IsNull(&gNvmDimmData->PMEMDev.Dimms, pDimmNode); pDimmNode = GetNextNode(&gNvmDimmData->PMEMDev.Dimms, pDimmNode)) { pDimm = DIMM_FROM_NODE(pDimmNode); ReturnCode = EFI_SUCCESS; /** Disconnect the device handles from the parent controller. (close the protocol opened with the BY_CHILD attribute) We are checking if both handles exist, because this function may be called more than once. **/ if (gDimmsUefiData[Index].DeviceHandle != NULL) { #ifndef OS_BUILD ReturnCode = gBS->CloseProtocol( ControllerHandle, &gEfiDevicePathProtocolGuid, pThis->DriverBindingHandle, gDimmsUefiData[Index].DeviceHandle ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to disconnect the child device, error = " FORMAT_EFI_STATUS "\n.", ReturnCode); } /** Uninstall all protocols from the child **/ ReturnCode = gBS->UninstallMultipleProtocolInterfaces( gDimmsUefiData[Index].DeviceHandle, &gEfiDevicePathProtocolGuid, gDimmsUefiData[Index].pDevicePath, NULL ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to uninstall the child device device path protocol, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); } #endif if (IsDimmManageable(pDimm)) { #ifndef OS_BUILD ReturnCode = gBS->UninstallMultipleProtocolInterfaces( gDimmsUefiData[Index].DeviceHandle, &gNvmDimmFirmwareManagementProtocolGuid, &gDimmsUefiData[Index].FirmwareManagementInstance, NULL ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to uninstall the child device firmware management protocol, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); } ReturnCode = gBS->UninstallMultipleProtocolInterfaces( gDimmsUefiData[Index].DeviceHandle, &gEfiStorageSecurityCommandProtocolGuid, &gDimmsUefiData[Index].StorageSecurityCommandInstance, NULL ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to uninstall the child device security command protocol, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); } ReturnCode = gBS->UninstallMultipleProtocolInterfaces( gDimmsUefiData[Index].DeviceHandle, &gEfiNvdimmLabelProtocolGuid, &gDimmsUefiData[Index].NvdimmLabelProtocolInstance, NULL ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to uninstall the child device NVDIMM label protocol, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); } #endif } gDimmsUefiData[Index].DeviceHandle = NULL; } if (gDimmsUefiData[Index].pDevicePath != NULL) { FreePool(gDimmsUefiData[Index].pDevicePath); gDimmsUefiData[Index].pDevicePath = NULL; } if (gDimmsUefiData[Index].pDimmName != NULL) { FreeUnicodeStringTable(gDimmsUefiData[Index].pDimmName); gDimmsUefiData[Index].pDimmName = NULL; } Index++; } #ifndef OS_BUILD ReturnCode = gBS->CloseProtocol( ControllerHandle, &gEfiDevicePathProtocolGuid, pThis->DriverBindingHandle, ControllerHandle ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to close the controller path protocol, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); } ReturnCode = gBS->CloseProtocol( ControllerHandle, &gNfitBindingProtocolGuid, pThis->DriverBindingHandle, ControllerHandle ); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to close the nfit binding protocol, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); } /** Uninstall NvmDimm Config Protocol **/ TempReturnCode = gBS->UninstallMultipleProtocolInterfaces(gNvmDimmData->DriverHandle, &gNvmDimmConfigProtocolGuid, &gNvmDimmDriverNvmDimmConfig, NULL); if (EFI_ERROR(TempReturnCode)) { NVDIMM_WARN("Failed to uninstall the NvmDimmConfig protocol, error = " FORMAT_EFI_STATUS ".\n", TempReturnCode); } TempReturnCode = UninitializeSmbusAccess(); if (EFI_ERROR(TempReturnCode)) { NVDIMM_DBG("Failed to uninstall smbus access, error = 0x%llx.", TempReturnCode); } TempReturnCode = SmbusDeinit(); if (EFI_ERROR(TempReturnCode)) { NVDIMM_DBG("Failed to Smbus deinit, error = " FORMAT_EFI_STATUS ".\n", TempReturnCode); } #endif /** Remove the DIMM from memory **/ TempReturnCode = FreeDimmList(); if (EFI_ERROR(TempReturnCode)) { NVDIMM_DBG("Failed to free dimm list, error = " FORMAT_EFI_STATUS ".\n", TempReturnCode); } /** Free PCAT tables memory **/ FreeParsedPcat(&gNvmDimmData->PMEMDev.pPcatHead); /** Free NFIT tables memory **/ FreeParsedNfit(&gNvmDimmData->PMEMDev.pFitHead); /** Free PMTT tables memory **/ FreeParsedPmtt(&gNvmDimmData->PMEMDev.pPmttHead); #ifdef OS_BUILD // Free an OS-only reference to the smbios table too FREE_POOL_SAFE(gSmbiosTable); #endif if (gNvmDimmData->HiiHandle != NULL) { HiiRemovePackages(gNvmDimmData->HiiHandle); gNvmDimmData->HiiHandle = NULL; } /** Clear the controller that we were started with **/ gNvmDimmData->ControllerHandle = NULL; Finish: #if _BullseyeCoverage #ifndef OS_BUILD cov_dumpData(); #endif // !OS_BUILD #endif // _BullseyeCoverage NVDIMM_DBG("Exiting DriverBindingStop, error = " FORMAT_EFI_STATUS ".\n", ReturnCode); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Returns the EFI_UNICODE_STRING_TABLE containing the Namespace Name. @param[in] NamespaceHandle the handle that we want to search for. @param[out] ppNamespaceName - pointer to where the function should place pointer to the Namespace EFI_UNICODE_STRING_TABLE. The value will be NULL if we will not find the Namespace. @retval EFI_SUCCESS if we have found the Namespace. @retval EFI_INVALID_PARAMETER if at least one of the input parameters equals NULL. @retval EFI_NOT_FOUND if we could not locate the Namespace. **/ EFI_STATUS GetNamespaceName( IN EFI_HANDLE NamespaceHandle, OUT EFI_UNICODE_STRING_TABLE **ppNamespaceName ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; #ifndef OS_BUILD LIST_ENTRY *pNamespaceNode = NULL; NAMESPACE *pNamespace = NULL; if (NamespaceHandle == NULL || ppNamespaceName == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *ppNamespaceName = NULL; LIST_FOR_EACH(pNamespaceNode, &gNvmDimmData->PMEMDev.Namespaces) { pNamespace = NAMESPACE_FROM_NODE(pNamespaceNode, NamespaceNode); if (pNamespace->BlockIoHandle == NamespaceHandle) { *ppNamespaceName = pNamespace->pNamespaceName; ReturnCode = EFI_SUCCESS; break; } } Finish: #endif return ReturnCode; } /** This function performs all necessary checks to determine what device type are the input parameters pointing to. This function is meant to be used by the ComponentName and DriverHealth protocols. @param[in] ControllerHandle EFI_HANDLE passed from the UEFI Libs, trying to resolve the device type for. @param[in] ChildHandle EFI_HANDLE passed from the UEFI Libs, trying to resolve the device type for. @param[out] pDimmPid is a pointer to the PID of the DIMM. @param[out] pResultDevice All if the ControllerHandle equals NULL. Controller if the ControllerHandle is our controller and the ChildHandle equals NULL. Dimm if the Controller equals to our controller and the ChildHandle equals to one of our DIMM handles. Namespace if the Controller equals to our controller and the ChildHandle equals to one of our block namespaces OR if the Controller equals to one of our DIMM handles and the ChildHandle equals to the block namespace that resides on the specific DIMM. Unknown all other cases. @retval EFI_SUCCESS - Matching manged device type for given handles @retval EFI_INVALID_PARAMETER - if at least one of the input parameters equals NULL. @retval EFI_UNSUPPORTED - Handles provided are not managed by driver **/ EFI_STATUS ResolveDeviceType( IN EFI_HANDLE ControllerHandle, IN EFI_HANDLE ChildHandle, OUT UINT16 *pDimmPid OPTIONAL, OUT NvmDeviceType *pResultDevice ) { EFI_STATUS ReturnCode = EFI_SUCCESS; #ifndef OS_BUILD NAMESPACE *pNamespace = NULL; UINT16 DimmPid = 0; NVDIMM_ENTRY(); if (ControllerHandle == NULL || pResultDevice == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pResultDevice = All; ReturnCode = EfiTestManagedDevice(ControllerHandle, gNvmDimmDriverDriverBinding.DriverBindingHandle, &gEfiDevicePathProtocolGuid); if (EFI_ERROR(ReturnCode)) { *pResultDevice = Unknown; // This is not our main controller but it can be our DIMM requesting the Namespace name. ReturnCode = EfiTestChildHandle(gNvmDimmData->ControllerHandle, ControllerHandle, &gEfiDevicePathProtocolGuid); if (EFI_ERROR(ReturnCode)) { // It is not the DIMM nor the Controller - unknown device goto Finish; } // The controller is one of our DIMMs, check if the given child is its child ReturnCode = EfiTestChildHandle(ControllerHandle, ChildHandle, &gEfiDevicePathProtocolGuid); if (EFI_ERROR(ReturnCode)) { // Again invalid set of handles goto Finish; } // This should be our Namespace - check it to be sure pNamespace = HandleToNamespace(ChildHandle); if (pNamespace == NULL) { // It was not our namespace, exit ReturnCode = EFI_UNSUPPORTED; goto Finish; } else { // It is our namespace *pResultDevice = Namespace; ReturnCode = EFI_SUCCESS; goto Finish; } } if (ChildHandle == NULL) { /** Lookup name of controller specified by ControllerHandle **/ *pResultDevice = Controller; goto Finish; } /** If ChildHandle is not NULL, then make sure this driver produced ChildHandle **/ ReturnCode = EfiTestChildHandle(ControllerHandle, ChildHandle, &gEfiDevicePathProtocolGuid); if (EFI_ERROR(ReturnCode)) { // The child handle is not our DIMM... but it can be our namespace. if (HandleToNamespace(ChildHandle) != NULL) { *pResultDevice = Namespace; ReturnCode = EFI_SUCCESS; } else { *pResultDevice = Unknown; } goto Finish; } // It is the DIMM *pResultDevice = Dimm; ReturnCode = HandleToPID(ChildHandle, NULL, &DimmPid); if (EFI_ERROR(ReturnCode)) { // The child handle is not our DIMM... but it can be our namespace if (HandleToNamespace(ChildHandle) != NULL) { *pResultDevice = Namespace; ReturnCode = EFI_SUCCESS; } else { *pResultDevice = Unknown; ReturnCode = EFI_UNSUPPORTED; } goto Finish; } if (pDimmPid != NULL) { *pDimmPid = DimmPid; } Finish: #endif NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** This function checks if we are managing the ChildHandle. If yes - it returns the DIMMs PID and the optional pointer to its EFI_UNICODE_STRING_TABLE @param[in] ChildHandle the handle that we want to search for. @param[out] Optional pointer to where the function should place pointer to the child EFI_UNICODE_STRING_TABLE. The value will be NULL if we will not find the DIMM @param[out] pDimmPid is a pointer to the PID of the DIMM. @retval EFI_SUCCESS if we have found the ChildHandle. @retval EFI_INVALID_PARAMETER if at least one of the input parameters equals NULL. @retval EFI_NOT_FOUND if we could not locate the ChildHandle. **/ EFI_STATUS HandleToPID( IN EFI_HANDLE ChildHandle, OUT EFI_UNICODE_STRING_TABLE **ppChildDeviceName OPTIONAL, OUT UINT16 *pDimmPid ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; #ifndef OS_BUILD UINT32 Index = 0; NVDIMM_ENTRY(); if (pDimmPid == NULL) { ReturnCode = EFI_INVALID_PARAMETER; } else if (ChildHandle != NULL) { *pDimmPid = 0; for (Index = 0; Index < MAX_DIMMS; Index++) { if (gDimmsUefiData[Index].DeviceHandle == ChildHandle) { if (ppChildDeviceName != NULL) { *ppChildDeviceName = gDimmsUefiData[Index].pDimmName; } *pDimmPid = gDimmsUefiData[Index].pDimm->DimmID; ReturnCode = EFI_SUCCESS; goto Finish; } } } if (ppChildDeviceName != NULL) { *ppChildDeviceName = NULL; } Finish: #endif NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Returns the index in the global DIMMs EFI Data array for the desired DIMM. The caller must provide one of the input commands, if the caller provides more than one parameter, only the last one will be taken under consideration. (the priority is: DimmHandle, pDimm, DimmPid) @param[in] DimmPid optional Dimm ID of the DIMM we want to get the index to. @param[in] pDimm optional pointer to the desired DIMM structure. @param[in] DimmHandle optional handle to the desired DIMM device. @retval DIMM_PID_INVALID is returned if no such DIMM has been found. @retval 0-based index of the desired DIMM in the global structure. **/ UINT16 GetDimmEfiDataIndex( IN UINT16 DimmPid, OPTIONAL IN DIMM *pDimm, OPTIONAL IN EFI_HANDLE DimmHandle OPTIONAL ) { #ifndef OS_BUILD EFI_STATUS ReturnCode = EFI_SUCCESS; UINT16 Index = 0; if (DimmHandle != NULL) { ReturnCode = HandleToPID(DimmHandle, NULL, &DimmPid); if EFI_ERROR(ReturnCode) { goto Finish; } } else if (pDimm != NULL) { DimmPid = pDimm->DimmID; } for (Index = 0; Index < MAX_DIMMS; Index++) { if (gDimmsUefiData[Index].pDimm->DimmID == DimmPid) { return Index; } } Finish: #endif return DIMM_PID_INVALID; } ipmctl-03.00.00.0485/DcpmPkg/driver/NvmDimmDriver.h000066400000000000000000000276451440615110200213450ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _NVMDIMM_DRIVER_H_ #define _NVMDIMM_DRIVER_H_ #include #include #include #ifndef OS_BUILD #include #endif #if defined(DYNAMIC_WA_ENABLE) #define DYNAMIC_WA_ENV_VARIABLE_NAME L"PMMD_FLAGS" #define WA_FLAG_ALWAYS_LOAD L"ALWAYS_LOAD" #define WA_FLAG_SIMICS L"SIMICS" #define WA_FLAG_UNLOAD_OTHER_DRIVERS L"UNLOAD" #define WA_FLAG_IGNORE_UID_NUMS L"IGNORE_UID_NUMBERS" #define WA_FLAG_NO_PCD_INIT L"NO_PCD_INIT" #endif /** This is the generated IFR binary data for each formset defined in VFR. This data array is ready to be used as input of HiiAddPackages() to create a package list (which contains Form packages, String packages, etc). **/ extern UINT8 NvmDimmDriverFormsBin[]; /** This is the generated String package data for all .UNI files. This data array is ready to be used as input of HiiAddPackages() to create a package list (which contains Form packages, String packages, etc). **/ extern UINT8 IntelOptanePMemDriverStrings[]; /** Libraries **/ #include #include #include #include #include #include #include #include #include /** UEFI Driver Model Protocols **/ #include #include #include #include #include #include #include #include #include #include /** Consumed Protocols **/ #include /** Produced Protocols **/ #include #include #include "NvmDimmConfig.h" #include #include #include #include /** Protocol instances **/ extern EFI_DRIVER_BINDING_PROTOCOL gNvmDimmDriverDriverBinding; extern EFI_COMPONENT_NAME2_PROTOCOL gNvmDimmDriverComponentName2; extern EFI_COMPONENT_NAME_PROTOCOL gNvmDimmDriverComponentName; extern EFI_DRIVER_DIAGNOSTICS2_PROTOCOL gNvmDimmDriverDriverDiagnostics2; extern EFI_DRIVER_DIAGNOSTICS_PROTOCOL gNvmDimmDriverDriverDiagnostics; extern EFI_DRIVER_HEALTH_PROTOCOL gNvmDimmDriverHealth; extern EFI_DCPMM_CONFIG2_PROTOCOL gNvmDimmDriverNvmDimmConfig; extern EFI_FIRMWARE_MANAGEMENT_PROTOCOL gNvmDimmFirmwareManagementProtocol; extern EFI_STORAGE_SECURITY_COMMAND_PROTOCOL gNvmDimmDriverStorageSecurityCommand; extern EFI_BLOCK_IO_PROTOCOL gNvmDimmDriverBlockIo; extern EFI_NVDIMM_LABEL_PROTOCOL gNvdimmLabelProtocol; extern EFI_DCPMM_PBR_PROTOCOL gNvmDimmDriverNvmDimmPbr; typedef struct _PMEM_DEV { LIST_ENTRY Dimms; LIST_ENTRY ISs; LIST_ENTRY ISsNfit; LIST_ENTRY Namespaces; BOOLEAN DimmSkuConsistency; BOOLEAN RegionsAndNsInitialized; BOOLEAN RegionsNfitInitialized; BOOLEAN NamespacesInitialized; BOOLEAN IsMemModeAllowedByBios; ParsedFitHeader *pFitHead; ParsedPcatHeader *pPcatHead; // Note: Only the PMTT 0.2 table is parsed and placed here! ParsedPmttHeader *pPmttHead; } PMEM_DEV; /** Specifies the alignments used by the driver. All of the values are in bytes. **/ typedef struct { UINT64 RegionPartitionAlignment; UINT64 RegionVolatileAlignment; UINT64 RegionPersistentAlignment; UINT64 PmNamespaceMinSize; } ALIGNMENT_SETTINGS; /** NvmDimmDriver Data structure **/ typedef struct { EFI_HANDLE Handle; EFI_HANDLE DriverHandle; EFI_HANDLE ControllerHandle; EFI_HANDLE HiiHandle; EFI_DEVICE_PATH_PROTOCOL *pControllerDevicePathInstance; #ifndef OS_BUILD /** BIOS Dcpmm protocol **/ EFI_DCPMM_PROTOCOL *pDcpmmProtocol; #endif // !OS_BUILD PMEM_DEV PMEMDev; /** NvmDimm data **/ EFI_DCPMM_CONFIG2_PROTOCOL NvmDimmConfig; /** This flag informs us if we installed the Device Path on the controller and should uninstall it or not. **/ BOOLEAN UninstallDevicePath; /** All of the minimal sizes and alignment values for the DIMM partitioning and Namespaces creation. **/ ALIGNMENT_SETTINGS Alignments; #if defined(DYNAMIC_WA_ENABLE) /** In case we need to try to load the driver when the binding handle is not present in the system, we can use this workaround to force-load the driver. Warning: the driver still requires NFIT to work properly. **/ BOOLEAN AlwaysLoadDriver; /** If we have set the 'AlwaysLoadDriver' flag, this indicates if we had already created ourselves a handle to bind. **/ BOOLEAN HandleCreated; /** If this driver and the HII driver are already loaded, unload them before loading **/ BOOLEAN UnloadExistingDrivers; /** Special set of workarounds to be applied in SIMICS environment **/ BOOLEAN SimicsWorkarounds; /** Disable checks of the same NVDIMM UID among dimms. Used when same SPD data is flashed without on many dimms. **/ BOOLEAN IgnoreTheSameUIDNumbers; /** During Initialization of the driver, avoid using any commands that touch the PCD data. **/ BOOLEAN PcdUsageDisabledOnInit; #endif /** DYNAMIC_WA_ENABLE **/ } NVMDIMMDRIVER_DATA; /** Structure to store EFI-related dimms data **/ typedef struct { EFI_HANDLE DeviceHandle; //!< The UEFI device handle that has the protocols installed EFI_DEVICE_PATH_PROTOCOL *pDevicePath; //!< The device path we need to release when unloading the driver DIMM *pDimm; //!< The DIMM shared information EFI_UNICODE_STRING_TABLE *pDimmName; //!< The localized dimm name table for the ComponentName2 Protocol /** We create an instance per DIMM so we are able to identify on which DIMM handle the protocol procedure was called. **/ EFI_FIRMWARE_MANAGEMENT_PROTOCOL FirmwareManagementInstance; EFI_STORAGE_SECURITY_COMMAND_PROTOCOL StorageSecurityCommandInstance; EFI_DRIVER_HEALTH_PROTOCOL DriverHealthProtocolInstance; EFI_BLOCK_IO_PROTOCOL BlockIoProtocolInstance; EFI_NVDIMM_LABEL_PROTOCOL NvdimmLabelProtocolInstance; } EFI_DIMMS_DATA; /** Type used to determine what kind of device is the particular set of Controller and Child handles aiming to. **/ typedef enum { Controller, Dimm, Namespace, All, Unknown } NvmDeviceType; /** This function performs all necessary checks to determine what device type are the input parameters pointing to. This function is meant to be used by the ComponentName and DriverHealth protocols. @param[in] ControllerHandle EFI_HANDLE passed from the UEFI Libs, trying to resolve the device type for. @param[in] ChildHandle EFI_HANDLE passed from the UEFI Libs, trying to resolve the device type for. @param[out] pDimmPid is a pointer to the PID of the DIMM. @param[out] pResultDevice All if the ControllerHandle equals NULL. Controller if the ControllerHandle is our controller and the ChildHandle equals NULL. Dimm if the Controller equals to our controller and the ChildHandle equals to one of our DIMM handles. Namespace if the Controller equals to our controller and the ChildHandle equals to one of our block namespaces OR if the Controller equals to one of our DIMM handles and the ChildHandle equals to the block namespace that resides on the specific DIMM. Unknown all other cases. @retval EFI_SUCCESS - Matching manged device type for given handles @retval EFI_INVALID_PARAMETER - if at least one of the input parameters equals NULL. @retval EFI_UNSUPPORTED - Handles provided are not managed by driver **/ EFI_STATUS ResolveDeviceType( IN EFI_HANDLE ControllerHandle, IN EFI_HANDLE ChildHandle, OUT UINT16 *pDimmPid OPTIONAL, OUT NvmDeviceType *pResultDevice ); /** AddStringToUnicodeTable Adds the provided string in the "en/eng" language to the provided UNICODE STRING TABLE. @param[in] pStringToAdd pointer to the Unicode string to be added. @param[in,out] ppTableToAddTo pointer to a pointer to the UNICODE STRING TABLE that the string needs to be added. @retval EFI_SUCCESS - everything went fine. @retval EFI_INVALID_PARAMETER - at least one of the input parameters equals NULL. Other return values from the AddUnicodeString2 function. **/ EFI_STATUS AddStringToUnicodeTable( IN CHAR16 *pStringToAdd, IN OUT EFI_UNICODE_STRING_TABLE **ppTableToAddTo ); /** Function that allows to "refresh" the existing DIMMs. If a DIMM is inaccessible, all of the ISs and namespaces that it was a part of will be removed. @param[in] DoDriverCleanup, if the caller wants namespaces cleaned up including unloading protocols @retval EFI_SUCCESS if all DIMMs are working. @retval EFI_INVALID_PARAMETER if any of pointer parameters in NULL @retval EFI_ABORTED if at least one DIMM is not responding. @retval EFI_OUT_OF_RESOURCES if the memory allocation fails. @retval EFI_NO_RESPONSE FW busy for one or more dimms **/ EFI_STATUS ReenumerateNamespacesAndISs( IN BOOLEAN DoDriverCleanup ); /** Function tries to remove all of the block namespaces protocols, then it removes all of the enumerated namespaces from the LSA and also the ISs. @retval EFI_SUCCESS the object were cleared successfully Error return codes from CleanBlockNamespaces function **/ EFI_STATUS CleanNamespacesAndISs( ); /** Returns the EFI_UNICODE_STRING_TABLE containing the Namespace Name. @param[in] NamespaceHandle the handle that we want to search for. @param[out] ppNamespaceName - pointer to where the function should place pointer to the Namespace EFI_UNICODE_STRING_TABLE. The value will be NULL if we will not find the Namespace. @retval EFI_SUCCeSS if we have found the Namespace. @retval EFI_INVALID_PARAMETER if at least one of the input parameters equals NULL. @retval EFI_NOT_FOUND if we could not locate the Namespace. **/ EFI_STATUS GetNamespaceName( IN EFI_HANDLE NamespaceHandle, OUT EFI_UNICODE_STRING_TABLE **ppNamespaceName ); /** This function checks if we are managing the ChildHandle. If yes - it returns the DIMMs PID. And the pointer to its EFI_UNICODE_STRING_TABLE @param[in] ChildHandle the handle that we want to search for. @param[out] Optional pointer to where the function should place pointer to the child EFI_UNICODE_STRING_TABLE. The value will be NULL if we will not find the DIMM @retval UINT16 DimmID of the DIMM or 0 of we did not find the device. **/ EFI_STATUS HandleToPID( IN EFI_HANDLE ChildHandle, OUT EFI_UNICODE_STRING_TABLE **ppChildDeviceName OPTIONAL, OUT UINT16 *pDimmPid ); /** This function checks if we are managing the ChildHandle. If yes - it returns the DIMMs PID and the optional pointer to its EFI_UNICODE_STRING_TABLE @param[in] ChildHandle the handle that we want to search for. @param[out] Optional pointer to where the function should place pointer to the child EFI_UNICODE_STRING_TABLE. The value will be NULL if we will not find the DIMM @param[out] pDimmPid is a pointer to the PID of the DIMM. @retval EFI_SUCCESS if we have found the ChildHandle. @retval EFI_INVALID_PARAMETER if at least one of the input parameters equals NULL. @retval EFI_NOT_FOUND if we could not locate the ChildHandle. **/ UINT16 GetDimmEfiDataIndex( IN UINT16 DimmPid, OPTIONAL IN DIMM *pDimm, OPTIONAL IN EFI_HANDLE DimmHandle OPTIONAL ); /** This function makes calls to the dimms required to initialize the driver. @retval EFI_SUCCESS if no errors. @retval EFI_xxxx depending on error encountered. **/ EFI_STATUS InitializeDimms(); /** Include files with function prototypes **/ #include "DriverBinding.h" #include "ComponentName.h" #include "DriverDiagnostics.h" #include "DriverHealth.h" #endif /** _NVMDIMM_DRIVER_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/driver/NvmDimmDriverData.h000066400000000000000000000023111440615110200221160ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _NVMDIMM_DRIVER_DATA_H_ #define _NVMDIMM_DRIVER_DATA_H_ #include #include #include #include #include #include #define SKU_SECURITY_FEATURE_SET 0 #define SKU_OVERWRITE_DIMM_FEATURE_SET 1 #define SKU_CONTROLLED_COUNTRY 2 /** Persistent Partition Settings masks **/ #define PARTITION_ENABLE_MASK BIT0 extern EFI_GUID gNvmDimmDevicePathGuid; /** DIMM GUID base. The GUID changes on each DIMM **/ #define NVMDIMM_DRIVER_NGNVM_GUID \ { 0xca5a7c11, 0x7278, 0x4bc3, {0x80, 0xcc, 0x40, 0x42, 0x70, 0x8d, 0x48, 0x6f }} extern EFI_GUID gNvmDimmNgnvmGuid; /** GUID for NvmDimmDriver Driver Variables for Get/Set via runtime services. **/ #define NVMDIMM_DRIVER_NGNVM_VARIABLE_GUID \ { 0x8986be7a, 0x212f, 0x427e, {0x81, 0xa5, 0x42, 0x0d, 0xab, 0xc7, 0x92, 0xdf}} extern EFI_GUID gNvmDimmNgnvmVariableGuid; extern EFI_GUID gIntelDimmConfigVariableGuid; extern EFI_GUID gIntelDimmPbrVariableGuid; extern EFI_GUID gIntelDimmPbrTagIdVariableguid; #endif /** _NVMDIMM_DRIVER_DATA_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/000077500000000000000000000000001440615110200202345ustar00rootroot00000000000000ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Device/000077500000000000000000000000001440615110200214335ustar00rootroot00000000000000ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Device/NvmFirmwareManagement.c000066400000000000000000000417211440615110200260360ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file NvmFirmwareManagement.c * @brief The file describes the UEFI Firmware Management Protocol support for Intel Optane Persistent Memory. **/ #include #include "NvmFirmwareManagement.h" #include #include #include #include extern NVMDIMMDRIVER_DATA *gNvmDimmData; EFI_GUID gNvmDimmFirmwareManagementProtocolGuid = EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID; EFI_GUID mNvmDimmFirmwareImageTypeGuid = EFI_DCPMM_FIRMWARE_IMAGE_TYPE_GUID; #define SUPPORTED_DESCRIPTOR_COUNT 1 #define NVDIMM_IMAGE_ID 1 #define DESCRIPTOR_VERSION_DEFAULT 1 // Compilers align structs front and back to the largest scalar member, // adding padding as necessary. // http://www.catb.org/esr/structure-packing/ // There's a UINT64, so we should be good for making sure the string (CHAR16) // buffers start on a 16-bit boundary // Also, these offsets are relative from the start of the // EFI_FIRMWARE_IMAGE_DESCRIPTOR struct #define NVDIMM_IMAGE_ID_NAME_BYTE_OFFSET sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR) // This value is specific to this file #define NVDIMM_IMAGE_DESCRIPTOR_MAX_STRING_LEN 255 #define NVDIMM_IMAGE_DESCRIPTOR_MAX_STRING_BUFFER_LENGTH (NVDIMM_IMAGE_DESCRIPTOR_MAX_STRING_LEN + 1) * sizeof(CHAR16) // Assume maximum size of strings appended to the end of the // struct and allocate the whole struct as one piece. The HII spec doesn't // specify where these strings should reside, so this is one of several possible // implementations. The caller should free the entire struct, which will free // the strings as well. #define NVDIMM_VERSION_NAME_BYTE_OFFSET NVDIMM_IMAGE_ID_NAME_BYTE_OFFSET + NVDIMM_IMAGE_DESCRIPTOR_MAX_STRING_BUFFER_LENGTH #define NVDIMM_IMAGE_DESCRIPTOR_SIZE NVDIMM_VERSION_NAME_BYTE_OFFSET + NVDIMM_IMAGE_DESCRIPTOR_MAX_STRING_BUFFER_LENGTH typedef struct _SET_IMAGE_ATTRIBUTES{ BOOLEAN Force; } SET_IMAGE_ATTRIBUTES; /* In addition to the function information from the library header. As for the Intel PMem module implementation, one PMem module stores only one Firmware, so the *DescriptorCount will be always 1. Even if there are more images on the FV we have access only to the one. */ /** Returns information about the current firmware image(s) of the device. @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. @param[in,out] ImageInfoSize A pointer to the size, in bytes, of the ImageInfo buffer. On input, this is the size of the buffer allocated by the caller. On output, it is the size of the buffer returned by the firmware if the buffer was large enough, or the size of the buffer needed to contain the image(s) information if the buffer was too small. @param[in,out] ImageInfo A pointer to the buffer in which firmware places the current image(s) information. The information is an array of EFI_FIRMWARE_IMAGE_DESCRIPTORs. See "Related Definitions". @param[out] DescriptorVersion A pointer to the location in which firmware returns the version number associated with the EFI_FIRMWARE_IMAGE_DESCRIPTOR. See "Related Definitions". @param[out] DescriptorCount A pointer to the location in which firmware returns the number of descriptors or firmware images within this device. @param[out] DescriptorSize A pointer to the location in which firmware returns the size, in bytes, of an individual EFI_FIRMWARE_IMAGE_DESCRIPTOR. @param[out] PackageVersion A version number that represents all the firmware images in the device. The format is vendor specific and new version must have a greater value than the old version. If PackageVersion is not supported, the value is 0xFFFFFFFF. A value of 0xFFFFFFFE indicates that package version comparison is to be performed using PackageVersionName. A value of 0xFFFFFFFD indicates that package version update is in progress. @param[out] PackageVersionName A pointer to a pointer to a null-terminated string representing the package version name. The buffer is allocated by this function with AllocatePool(), and it is the caller's responsibility to free it with a call to FreePool(). @retval EFI_SUCCESS The image information was successfully returned. @retval EFI_BUFFER_TOO_SMAL The ImageInfo buffer was too small. The current buffer size needed to hold the image(s) information is returned in ImageInfoSize. @retval EFI_INVALID_PARAMETER The ImageInfoSize is NULL. @retval EFI_DEVICE_ERROR Valid information could not be returned. Possible corrupted image. **/ EFI_STATUS EFIAPI GetImageInfo ( IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, IN OUT UINTN *pImageInfoSize, IN OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *pImageInfo, OUT UINT32 *pDescriptorVersion, OUT UINT8 *pDescriptorCount, OUT UINTN *pDescriptorSize, OUT UINT32 *pPackageVersion, OUT CHAR16 **ppPackageVersionName ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimm = NULL; FW_VERSION_UNION Uint32Version; CHAR16 *ImageIdName; CHAR16 *VersionName; NVDIMM_ENTRY(); SetMem(&Uint32Version, sizeof(Uint32Version), 0x0); if (NULL == pImageInfoSize || NULL == This || (NULL == pImageInfo && *pImageInfoSize >= NVDIMM_IMAGE_DESCRIPTOR_SIZE)) { goto Finish; } //per UEFI spec, NULL pImageInfo with 0 pImageInfoSize is used to obtain size if (NULL == pImageInfo && 0 == *pImageInfoSize) { *pImageInfoSize = NVDIMM_IMAGE_DESCRIPTOR_SIZE; ReturnCode = EFI_SUCCESS; goto Finish; } if (*pImageInfoSize < NVDIMM_IMAGE_DESCRIPTOR_SIZE) { *pImageInfoSize = NVDIMM_IMAGE_DESCRIPTOR_SIZE; ReturnCode = EFI_BUFFER_TOO_SMALL; goto Finish; } pDimm = GET_DIMM_FROM_INSTANCE(This)->pDimm; if (pDimm == NULL) { goto Finish; } /* Here we are packing the 8-bit values into 6-bit containers to fit in the UINT32. If the versions will ever go above the 6-bit integer, we will lose that information, but there is nothing else that we can do and we have to agree with that. */ Uint32Version.PackedVersion.FwProduct = pDimm->FwVer.FwProduct; Uint32Version.PackedVersion.FwRevision = pDimm->FwVer.FwRevision; Uint32Version.PackedVersion.FwSecurityVersion = pDimm->FwVer.FwSecurityVersion; Uint32Version.PackedVersion.FwBuild = pDimm->FwVer.FwBuild; pImageInfo[0].ImageIndex = 1; pImageInfo[0].ImageTypeId = mNvmDimmFirmwareImageTypeGuid; pImageInfo[0].ImageId = NVDIMM_IMAGE_ID; // Copy the image id name string to a region after the end of the struct ImageIdName = (CHAR16 *)(((UINT8 *)&pImageInfo[0]) + NVDIMM_IMAGE_ID_NAME_BYTE_OFFSET); StrnCpyS(ImageIdName, NVDIMM_IMAGE_ID_NAME_LEN, NVDIMM_IMAGE_ID_NAME, NVDIMM_IMAGE_ID_NAME_LEN - 1); // Pointer to that region pImageInfo[0].ImageIdName = ImageIdName; pImageInfo[0].Version = Uint32Version.AsUint32; // Creates fw version string and writes to a provided location VersionName = (CHAR16 *)(((UINT8 *)&pImageInfo[0]) + NVDIMM_VERSION_NAME_BYTE_OFFSET); ConvertFwVersion(VersionName, pDimm->FwVer.FwProduct, pDimm->FwVer.FwRevision, pDimm->FwVer.FwSecurityVersion, pDimm->FwVer.FwBuild); pImageInfo[0].VersionName = VersionName; pImageInfo[0].Size = MAX_FIRMWARE_IMAGE_SIZE_B; // No planned way to get installed image size. // Set allowed maximum. pImageInfo[0].AttributesSupported = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | IMAGE_ATTRIBUTE_RESET_REQUIRED | IMAGE_ATTRIBUTE_IN_USE; pImageInfo[0].AttributesSetting = pImageInfo[0].AttributesSupported; // We use the Firmware Major API version as the compatibility indicator pImageInfo[0].Compatibilities = pDimm->FwVer.FwApiMajor; if (pPackageVersion != NULL) { *pPackageVersion = PACKAGE_VERSION_DEFINED_BY_PACKAGE_NAME; } if (ppPackageVersionName != NULL) { // Use existing version name string *ppPackageVersionName = AllocateCopyPool(FW_VERSION_LEN * sizeof(CHAR16), VersionName); } if (pDescriptorSize != NULL) { *pDescriptorSize = NVDIMM_IMAGE_DESCRIPTOR_SIZE; } if (pDescriptorCount != NULL) { *pDescriptorCount = SUPPORTED_DESCRIPTOR_COUNT; } if (pDescriptorVersion != NULL) { *pDescriptorVersion = DESCRIPTOR_VERSION_DEFAULT; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Updates the firmware image of the PMem module. @remarks If ARS is in progress on any target PMem module, an attempt will be made to abort ARS and to proceed with the firmware update. @remarks A reboot is required to activate the updated firmware image and is recommended to ensure ARS runs to completion. @param[in] pThis is a pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. @param[in] ImageIndex A unique number identifying the firmware image(s) within the device. The number is between 1 and DescriptorCount. @param[in] Image Points to the new image. @param[in] ImageSize Size of the new image in bytes @param[in] VendorCode This enables vendor to implement vendor-specific firmware image update policy. Null indicates the caller did not specify the policy or use the default policy. @param[in] Progress A function used by the driver to report the progress of the firmware update. @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more details for the aborted operation. The buffer is allocated by this function with AllocatePool(), and it is the caller's responsibility to free it with a call to FreePool(). @retval EFI_SUCCESS The device was successfully updated with the new image. @retval EFI_ABORTED The operation is aborted. @retval EFI_INVALID_PARAMETER The Image was NULL. @retval EFI_UNSUPPORTED The operation is not supported. @retval EFI_SECURITY_VIOLATION The operation could not be performed due to an authentication failure. **/ EFI_STATUS EFIAPI SetImage ( IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, IN UINT8 ImageIndex, IN CONST VOID *Image, IN UINTN ImageSize, IN CONST VOID *VendorCode, IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress, OUT CHAR16 **AbortReason ) { EFI_STATUS ReturnCode = EFI_SUCCESS; CHAR16 pImageSizeError[] = L"Error: The image size is too large."; CHAR16 pImageContentError[] = L"Error: Invalid image file."; CHAR16 pImageVersionError[] = L"Error: Firmware version too low. Force required."; NVM_STATUS Status = NVM_ERR_OPERATION_NOT_STARTED; CONST CHAR16 *pSingleStatusCodeMessage = NULL; SET_IMAGE_ATTRIBUTES *SetImageAttributes = (SET_IMAGE_ATTRIBUTES *)VendorCode; COMMAND_STATUS *pCommandStatus = NULL; BOOLEAN Force = FALSE; NVDIMM_ENTRY(); if (NULL == This || NULL == Image || NULL == AbortReason || ImageIndex < 1 || ImageIndex > SUPPORTED_DESCRIPTOR_COUNT) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } CHECK_RESULT(InitializeCommandStatus(&pCommandStatus), Finish); if (ImageSize > MAX_FIRMWARE_IMAGE_SIZE_B) { *AbortReason = AllocateCopyPool(sizeof(pImageSizeError), pImageSizeError); ReturnCode = EFI_ABORTED; goto Finish; } if (ImageSize < sizeof(NVM_FW_IMAGE_HEADER)) { *AbortReason = AllocateCopyPool(sizeof(pImageContentError), pImageContentError); ReturnCode = EFI_ABORTED; goto Finish; } /* Inform the caller that we do not support progress reporting. */ if (Progress != NULL) { Progress(0); } if (NULL != SetImageAttributes) { Force = SetImageAttributes->Force; } ReturnCode = ValidateImageVersion((NVM_FW_IMAGE_HEADER *)Image, Force, GET_DIMM_FROM_INSTANCE(This)->pDimm, &Status, pCommandStatus); if (EFI_ERROR(ReturnCode)) { if (Status == NVM_ERR_FIRMWARE_TOO_LOW_FORCE_REQUIRED) { *AbortReason = AllocateCopyPool (StrSize(pImageVersionError), pImageVersionError); } goto Finish; } ReturnCode = FwCmdUpdateFw(GET_DIMM_FROM_INSTANCE(This)->pDimm, Image, ImageSize, &Status, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_ABORTED; pSingleStatusCodeMessage = GetSingleNvmStatusCodeMessage(gNvmDimmData->HiiHandle, Status); if (pSingleStatusCodeMessage != NULL) { *AbortReason = AllocateCopyPool (StrSize(pSingleStatusCodeMessage), pSingleStatusCodeMessage); } NVDIMM_WARN("Could not update the firmware on the DIMM(s).\n"); goto Finish; } Finish: FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pSingleStatusCodeMessage); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Returns information about the firmware package on the specified PMem module. @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. @param[out] PackageVersion A version number that represents all the firmware images in the device. The format is vendor specific and new version must have a greater value than the old version. If PackageVersion is not supported, the value is 0xFFFFFFFF. A value of 0xFFFFFFFE indicates that package version comparison is to be performed using PackageVersionName. A value of 0xFFFFFFFD indicates that package version update is in progress. @param[out] PackageVersionName A pointer to a pointer to a null-terminated string representing the package version name. The buffer is allocated by this function with AllocatePool(), and it is the caller's responsibility to free it with a call to FreePool(). @param[out] PackageVersionNameMaxLen The maximum length of package version name if device supports update of package version name. A value of 0 indicates the device does not support update of package version name. Length is the number of Unicode characters, including the terminating null character. @param[out] AttributesSupported Package attributes that are supported by this device. See "Package Attribute Definitions" for possible returned values of this parameter. A value of 1 indicates the attribute is supported and the current setting value is indicated in AttributesSetting. A value of 0 indicates the attribute is not supported and the current setting value in AttributesSetting is meaningless. @param[out] AttributesSetting Package attributes. See "Package Attribute Definitions" for possible returned values of this parameter. @retval EFI_SUCCESS The image information was successfully returned. @retval EFI_UNSUPPORTED The operation is not supported. **/ EFI_STATUS EFIAPI GetPackageInfo ( IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, OUT UINT32 *PackageVersion, OUT CHAR16 **PackageVersionName, OUT UINT32 *PackageVersionNameMaxLen, OUT UINT64 *AttributesSupported, OUT UINT64 *AttributesSetting ) { DIMM *pDimm = NULL; CHAR16 FwVersion[FW_VERSION_LEN]; CONST UINT64 NvmFwMgmtAttributesSupported = PACKAGE_ATTRIBUTE_VERSION_UPDATABLE | PACKAGE_ATTRIBUTE_RESET_REQUIRED; SetMem(&FwVersion, sizeof(FwVersion), 0x0); if (NULL == This) { return EFI_INVALID_PARAMETER; } if (NULL != PackageVersion) { *PackageVersion = PACKAGE_VERSION_DEFINED_BY_PACKAGE_NAME; } if (NULL != PackageVersionName) { pDimm = GET_DIMM_FROM_INSTANCE(This)->pDimm; ConvertFwVersion(FwVersion, pDimm->FwVer.FwProduct, pDimm->FwVer.FwRevision, pDimm->FwVer.FwSecurityVersion, pDimm->FwVer.FwBuild); *PackageVersionName = AllocateCopyPool(sizeof(FwVersion), FwVersion); } /* Zero means that we don't support updating the Package Version Name. */ if (NULL != PackageVersionNameMaxLen) { *PackageVersionNameMaxLen = 0; } if (NULL != AttributesSupported) { *AttributesSupported = NvmFwMgmtAttributesSupported; } /* We can't change the settings so what is supported is also set. */ if (NULL != AttributesSetting) { *AttributesSetting = NvmFwMgmtAttributesSupported; } /* This function returns UNSUPPORTED or SUCCESS, since we support it, we can't return any error codes. If there is an error, it will be passed by the OUT parameters. */ return EFI_SUCCESS; } /** Retrieves a copy of the current firmware image of the device. @remarks This is not supported. @retval EFI_UNSUPPORTED The operation is not supported. **/ EFI_STATUS EFIAPI GetImage ( IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, IN UINT8 ImageIndex, IN OUT VOID *Image, IN OUT UINTN *ImageSize ) { /* We do not support this callback. */ return EFI_UNSUPPORTED; } /** Checks if the firmware image is valid for the device. @remarks This is not supported. @retval EFI_UNSUPPORTED The operation is not supported. **/ EFI_STATUS EFIAPI CheckImage ( IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, IN UINT8 ImageIndex, IN CONST VOID *Image, IN UINTN ImageSize, OUT UINT32 *ImageUpdatable ) { /* We do not support this callback. */ return EFI_UNSUPPORTED; } /** Updates information about the firmware package. @remarks This is not supported. @retval EFI_UNSUPPORTED The operation is not supported. **/ EFI_STATUS EFIAPI SetPackageInfo ( IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, IN CONST VOID *Image, IN UINTN ImageSize, IN CONST VOID *VendorCode, IN UINT32 PackageVersion, IN CONST CHAR16 *PackageVersionName ) { /* We do not support this callback. */ return EFI_UNSUPPORTED; } EFI_FIRMWARE_MANAGEMENT_PROTOCOL gNvmDimmFirmwareManagementProtocol = { GetImageInfo, GetImage, SetImage, CheckImage, GetPackageInfo, SetPackageInfo }; ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Device/NvmFirmwareManagement.h000066400000000000000000000021671440615110200260440ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _NVM_FIRMWARE_MANAGEMENT_H_ #define _NVM_FIRMWARE_MANAGEMENT_H_ #include #include extern EFI_GUID gNvmDimmFirmwareManagementProtocolGuid; extern EFI_GUID mNvmDimmFirmwareImageTypeGuid; /* {c376fe51-aad4-4b40-9ed2-f775cbbdc6a1} */ #define EFI_DCPMM_FIRMWARE_IMAGE_TYPE_GUID \ {0xc376fe51, 0xaad4, 0x4b40, {0x9e, 0xd2, 0xf7, 0x75, 0xcb, 0xbd, 0xc6, 0xa1}} #define GET_DIMM_FROM_INSTANCE(InstanceAddress) BASE_CR(InstanceAddress, EFI_DIMMS_DATA, FirmwareManagementInstance) #define PACKAGE_VERSION_DEFINED_BY_PACKAGE_NAME 0xFFFFFFFE #define NVDIMM_IMAGE_ID_NAME L"Intel persistent memory" #define NVDIMM_IMAGE_ID_NAME_LEN 24 // including null char #pragma pack(push) #pragma pack(1) typedef struct { UINT8 FwProduct : 6; UINT8 FwRevision : 6; UINT8 FwSecurityVersion : 6; UINT16 FwBuild : 14; } FW_VERSION_INFORMATION_PACKED; #pragma pack(pop) typedef union { FW_VERSION_INFORMATION_PACKED PackedVersion; UINT32 AsUint32; } FW_VERSION_UNION; #endif /** _NVM_FIRMWARE_MANAGEMENT_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Device/StorageSecurityCommand.c000066400000000000000000000166331440615110200262430ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include "NvmDimmDriver.h" #include "NvmSecurity.h" #include /** Supported Security Protocols & Protocol Specific parameters **/ #define SECURITY_PROTOCOL_INFORMATION 0x00 //!< Supported protocols information #define CMD_SUPPORTED_SECURITY_PROTOCOL_LIST 0x0000 //!< Retrieve supported security protocols #define SECURITY_PROTOCOL_FW_COMMANDS 0xFA //!< Vendor specific protocol #define CMD_OPCODE_MASK 0xFF00 //!< 2nd byte contains Opcode #define CMD_SUBOPCODE_MASK 0x00FF //!< 1st byte contains SubOpcode #define SUPPORTED_PROTOCOL_LIST_LENGTH 2 //!< Number of supported protocols #pragma pack(push) #pragma pack(1) typedef struct { UINT8 Reserved[6]; UINT16 Length; UINT8 Protocol[SUPPORTED_PROTOCOL_LIST_LENGTH]; } PROTOCOL_INFORMATION; #pragma pack(pop) /** Instance of StorageSecurityCommandProtocol **/ EFI_STATUS EFIAPI ReceiveData ( IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, IN UINT32 MediaId, //!< Ignored in this implementation IN UINT64 Timeout, //!< Timeout passed to PassThru protocol IN UINT8 SecurityProtocol, //!< Specifies which security protocol is being used IN UINT16 SecurityProtocolSpecificData, //!< Command Opcode & SubOpcode in case of FW commands IN UINTN PayloadBufferSize, OUT VOID *PayloadBuffer, OUT UINTN *PayloadTransferSize) { NVDIMM_ENTRY(); EFI_STATUS ReturnCode = EFI_UNSUPPORTED; UINT32 SecurityState = 0; UINT8 Opcode, SubOpcode; PROTOCOL_INFORMATION SupportedProtocolsData = { .Reserved = {0}, .Length = SUPPORTED_PROTOCOL_LIST_LENGTH, .Protocol = {SECURITY_PROTOCOL_INFORMATION, SECURITY_PROTOCOL_FW_COMMANDS} }; if (NULL == This) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } EFI_DIMMS_DATA *Dimm = BASE_CR(This, EFI_DIMMS_DATA, StorageSecurityCommandInstance); if ((PayloadBuffer == NULL || PayloadTransferSize == NULL) && PayloadBufferSize != 0) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (SecurityProtocol != SECURITY_PROTOCOL_INFORMATION && SecurityProtocol != SECURITY_PROTOCOL_FW_COMMANDS) { NVDIMM_WARN("Security protocol id %d not supported by ReceiveData function", SecurityProtocol); ReturnCode = EFI_UNSUPPORTED; goto Finish; } /** Security Protocol Information **/ if (SecurityProtocol == SECURITY_PROTOCOL_INFORMATION) { if (SecurityProtocolSpecificData == CMD_SUPPORTED_SECURITY_PROTOCOL_LIST) { NVDIMM_DBG("Retrieving supported security protocol list ..."); if (PayloadBuffer == NULL || PayloadTransferSize == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // check amount of data to be returned *PayloadTransferSize = sizeof(SupportedProtocolsData); if (*PayloadTransferSize > PayloadBufferSize) { NVDIMM_DBG("Buffer too small"); *PayloadTransferSize = PayloadBufferSize; ReturnCode = EFI_WARN_BUFFER_TOO_SMALL; } else { ReturnCode = EFI_SUCCESS; } CopyMem_S(PayloadBuffer, PayloadBufferSize, &SupportedProtocolsData, *PayloadTransferSize); goto Finish; } else { NVDIMM_WARN("Command not not supported: 0x%x", SecurityProtocolSpecificData); ReturnCode = EFI_UNSUPPORTED; goto Finish; } } /** FW Commands **/ if (SecurityProtocol == SECURITY_PROTOCOL_FW_COMMANDS) { Opcode = (UINT8) ((SecurityProtocolSpecificData & CMD_OPCODE_MASK) >> 8); SubOpcode = (UINT8) (SecurityProtocolSpecificData & CMD_SUBOPCODE_MASK); NVDIMM_DBG("FW command: Opcode=%x, SubOpcode=%x", Opcode, SubOpcode); if (Opcode == PtGetSecInfo) { if (SubOpcode == SubopGetSecState) { if (PayloadBuffer == NULL || PayloadTransferSize == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = GetDimmSecurityState(Dimm->pDimm, PT_TIMEOUT_INTERVAL, &SecurityState); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetDimmSecurityState, status=" FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } // check amount of data to be returned *PayloadTransferSize = sizeof(SecurityState); if (*PayloadTransferSize > PayloadBufferSize) { NVDIMM_DBG("Buffer too small"); *PayloadTransferSize = PayloadBufferSize; ReturnCode = EFI_WARN_BUFFER_TOO_SMALL; } // write data to return buffer CopyMem_S(PayloadBuffer, PayloadBufferSize, &SecurityState, *PayloadTransferSize); } else { //SubOpcode not supported NVDIMM_WARN("Command not supported: Opcode=%x, SubOpcode=%x", Opcode, SubOpcode); ReturnCode = EFI_UNSUPPORTED; goto Finish; } } else { //Opcode not supported NVDIMM_WARN("Command not supported: Opcode=%x, SubOpcode=%x", Opcode, SubOpcode); ReturnCode = EFI_UNSUPPORTED; goto Finish; } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } EFI_STATUS EFIAPI SendData ( IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, IN UINT32 MediaId, IN UINT64 Timeout, IN UINT8 SecurityProtocol, IN UINT16 SecurityProtocolSpecificData, IN UINTN PayloadBufferSize, IN VOID *PayloadBuffer) { NVDIMM_ENTRY(); EFI_STATUS ReturnCode = EFI_SUCCESS; UINT8 Opcode, SubOpcode; if (NULL == This) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } EFI_DIMMS_DATA *Dimm = BASE_CR(This, EFI_DIMMS_DATA, StorageSecurityCommandInstance); if ((PayloadBuffer == NULL) && (PayloadBufferSize != 0)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (SecurityProtocol != SECURITY_PROTOCOL_FW_COMMANDS) { NVDIMM_DBG("Security protocol id %d not supported by SendData function", SecurityProtocol); ReturnCode = EFI_UNSUPPORTED; goto Finish; } /** FW Commands **/ if (SecurityProtocol == SECURITY_PROTOCOL_FW_COMMANDS) { Opcode = (UINT8) ((SecurityProtocolSpecificData & CMD_OPCODE_MASK) >> 8); SubOpcode = (UINT8) (SecurityProtocolSpecificData & CMD_SUBOPCODE_MASK); NVDIMM_DBG("FW command: Opcode=%x, SubOpcode=%x", Opcode, SubOpcode); if (Opcode == PtSetSecInfo) { if (SubOpcode == SubopSecEraseUnit) { /** Need to call WBINVD before secure erase **/ AsmWbinvd(); } ReturnCode = SetDimmSecurityState(Dimm->pDimm, Opcode, SubOpcode, (UINT16)PayloadBufferSize, PayloadBuffer, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on SetDimmSecurityState, status=" FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } if ((SubOpcode == SubopSecEraseUnit) || (SubOpcode == SubopUnlockUnit)) { /** Need to call WBINVD after unlock or secure erase **/ AsmWbinvd(); } } else { //Opcode not supported NVDIMM_WARN("Command not supported: Opcode=%x, SubOpcode=%x", Opcode, SubOpcode); ReturnCode = EFI_UNSUPPORTED; goto Finish; } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } EFI_STORAGE_SECURITY_COMMAND_PROTOCOL gNvmDimmDriverStorageSecurityCommand = { ReceiveData, SendData }; ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Driver/000077500000000000000000000000001440615110200214675ustar00rootroot00000000000000ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Driver/ComponentName.h000066400000000000000000000071751440615110200244150ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _COMPONENT_NAME_H_ #define _COMPONENT_NAME_H_ /** Retrieves a Unicode string that is the user-readable name of the EFI Driver. @param pThis A pointer to the EFI_COMPONENT_NAME_PROTOCOL2 instance. @param pLanguage A pointer to an ASCII string containing the ISO 639-2 or the RFC 4646 language code. This is the language of the driver name that the caller is requesting, and it must match one of the languages specified in SupportedLanguages. The number of languages supported by a driver is up to the driver writer. @param ppDriverName A pointer to the Unicode string to return. This Unicode string is the name of the driver specified by pThis in the language specified by Language. @retval EFI_SUCCESS The Unicode string for the Driver specified by pThis and the language specified by pLanguage was returned in ppDriverName. @retval EFI_INVALID_PARAMETER pLanguage is NULL. @retval EFI_INVALID_PARAMETER ppDriverName is NULL. @retval EFI_UNSUPPORTED The driver specified by pThis does not support the language specified by pLanguage. **/ EFI_STATUS EFIAPI NvmDimmDriverComponentNameGetDriverName ( IN EFI_COMPONENT_NAME2_PROTOCOL *pThis, IN CHAR8 *pLanguage, OUT CHAR16 **ppDriverName ); /** Retrieves a Unicode string that is the user readable name of the controller that is being managed by an EFI Driver. @param pThis A pointer to the EFI_COMPONENT_NAME2_PROTOCOL instance. @param ControllerHandle The handle of a controller that the driver specified by This is managing. This handle specifies the controller whose name is to be returned. @param ChildHandle The handle of the child controller to retrieve the name for. This is an optional parameter that may be NULL. It will be NULL for device drivers. It will also be NULL for a bus drivers that wish to retrieve the name of the bus controller. It will not be NULL for a bus driver that wishes to retrieve the name of a child controller. @param pLanguage A pointer to a three character ISO 639-2 language identifier. This is the language of the controller name that the caller is requesting, and it must match one of the languages specified in SupportedLanguages. The number of languages supported by a driver is up to the driver writer. @param ppControllerName A pointer to the Unicode string to return. This Unicode string is the name of the controller specified by ControllerHandle and ChildHandle in the language specified by Language, from the point of view of the driver specified by This. @retval EFI_SUCCESS The Unicode string for the user-readable name in the language specified by Language for the driver specified by This was returned in DriverName. @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. @retval EFI_INVALID_PARAMETER Language is NULL. @retval EFI_INVALID_PARAMETER ControllerName is NULL. @retval EFI_UNSUPPORTED The driver specified by This is not currently managing the controller specified by ControllerHandle and ChildHandle. @retval EFI_UNSUPPORTED The driver specified by This does not support the language specified by Language. **/ EFI_STATUS EFIAPI NvmDimmDriverComponentNameGetControllerName ( IN EFI_COMPONENT_NAME2_PROTOCOL *pThis, IN EFI_HANDLE ControllerHandle, IN EFI_HANDLE ChildHandle OPTIONAL, IN CHAR8 *pLanguage, OUT CHAR16 **ppControllerName ); #endif /* _COMPONENT_NAME_H_ */ ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Driver/DriverBinding.h000066400000000000000000000157031440615110200243740ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DRIVER_BINDING_H_ #define _DRIVER_BINDING_H_ /** Tests to see if this driver supports a given controller. If a child device is provided, it further tests to see if this driver supports creating a handle for the specified child device. This function checks to see if the driver specified by This supports the device specified by ControllerHandle. Drivers will typically use the device path attached to ControllerHandle and/or the services from the bus I/O abstraction attached to ControllerHandle to determine if the driver supports ControllerHandle. This function may be called many times during platform initialization. In order to reduce boot times, the tests performed by this function must be very small, and take as little time as possible to execute. This function must not change the state of any hardware devices, and this function must be aware that the device specified by ControllerHandle may already be managed by the same driver or a different driver. This function must match its calls to AllocatePages() with FreePages(), AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). Because ControllerHandle may have been previously started by the same driver, if a protocol is already in the opened state, then it must not be closed with CloseProtocol(). This is required to guarantee the state of ControllerHandle is not modified by this function. @param[in] pThis A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. @param[in] ControllerHandle The handle of the controller to test. This handle must support a protocol interface that supplies an I/O abstraction to the driver. @param[in] pRemainingDevicePath A pointer to the remaining portion of a device path. This parameter is ignored by device drivers, and is optional for bus drivers. For bus drivers, if this parameter is not NULL, then the bus driver must determine if the bus controller specified by ControllerHandle and the child controller specified by RemainingDevicePath are both supported by this bus driver. @retval EFI_SUCCESS The device specified by ControllerHandle and RemainingDevicePath is supported by the driver specified by This. @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and RemainingDevicePath is already being managed by the driver specified by This. @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and RemainingDevicePath is already being managed by a different driver or an application that requires exclusive access. Currently not implemented. @retval EFI_UNSUPPORTED The device specified by ControllerHandle and RemainingDevicePath is not supported by the driver specified by This. **/ EFI_STATUS EFIAPI NvmDimmDriverDriverBindingSupported( IN EFI_DRIVER_BINDING_PROTOCOL *pThis, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *pRemainingDevicePath OPTIONAL ); /** Starts a device controller or a bus controller. The Start() function is designed to be invoked from the EFI boot service ConnectController(). As a result, much of the error checking on the parameters to Start() has been moved into this common boot service. It is legal to call Start() from other locations, but the following calling restrictions must be followed, or the system behavior will not be deterministic. 1. ControllerHandle must be a valid EFI_HANDLE. 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned EFI_DEVICE_PATH_PROTOCOL. 3. Prior to calling Start(), the Supported() function for the driver specified by This must have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. @param[in] pThis A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. @param[in] ControllerHandle The handle of the controller to start. This handle must support a protocol interface that supplies an I/O abstraction to the driver. @param[in] pRemainingDevicePath A pointer to the remaining portion of a device path. This parameter is ignored by device drivers, and is optional for bus drivers. For a bus driver, if this parameter is NULL, then handles for all the children of Controller are created by this driver. If this parameter is not NULL and the first Device Path Node is not the End of Device Path Node, then only the handle for the child device specified by the first Device Path Node of RemainingDevicePath is created by this driver. If the first Device Path Node of RemainingDevicePath is the End of Device Path Node, no child handle is created by this driver. @retval EFI_SUCCESS The device was started. @retval EFI_DEVICE_ERROR The device could not be started due to a device error. Currently not implemented. @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. @retval Others The driver failed to start the device. **/ EFI_STATUS EFIAPI NvmDimmDriverDriverBindingStart( IN EFI_DRIVER_BINDING_PROTOCOL *pThis, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *pRemainingDevicePath OPTIONAL ); /** Stops a device controller or a bus controller. The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). As a result, much of the error checking on the parameters to Stop() has been moved into this common boot service. It is legal to call Stop() from other locations, but the following calling restrictions must be followed, or the system behavior will not be deterministic. 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this same driver's Start() function. 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid EFI_HANDLE. In addition, all of these handles must have been created in this driver's Start() function, and the Start() function must have called OpenProtocol() on ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. @param[in] pThis A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. @param[in] ControllerHandle A handle to the device being stopped. The handle must support a bus specific I/O protocol for the driver to use to stop the device. @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. @param[in] pChildHandleBuffer An array of child handles to be freed. May be NULL if NumberOfChildren is 0. @retval EFI_SUCCESS The device was stopped. @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. **/ EFI_STATUS EFIAPI NvmDimmDriverDriverBindingStop( IN EFI_DRIVER_BINDING_PROTOCOL *pThis, IN EFI_HANDLE ControllerHandle, IN UINTN NumberOfChildren, IN EFI_HANDLE *pChildHandleBuffer OPTIONAL ); #endif /* _DRIVER_BINDING_H_ */ ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Driver/DriverDiagnostics.h000066400000000000000000000070571440615110200252740ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DRIVER_DIAGNOSTICS_H_ #define _DRIVER_DIAGNOSTICS_H_ /** Runs diagnostics on a controller. @param pThis A pointer to the EFI_DRIVER_DIAGNOSTICS2_PROTOCOL instance. @param ControllerHandle The handle of the controller to run diagnostics on. @param ChildHandle The handle of the child controller to run diagnostics on This is an optional parameter that may be NULL. It will be NULL for device drivers. It will also be NULL for bus drivers that wish to run diagnostics on the bus controller. It will not be NULL for a bus driver that wishes to run diagnostics on one of its child controllers. @param DiagnosticType Indicates the type of diagnostics to perform on the controller specified by ControllerHandle and ChildHandle. See "Related Definitions" for the list of supported types. @param pLanguage A pointer to a Null-terminated ASCII string array indicating the language. This is the language of the driver name that the caller is requesting, and it must match one of the languages specified in SupportedLanguages. The number of languages supported by a driver is up to the driver writer. Language is specified in RFC 4646 language code format. @param ppErrorType A GUID that defines the format of the data returned in Buffer. @param pBufferSize The size, in bytes, of the data returned in Buffer. @param ppBuffer A buffer that contains a Null-terminated Unicode string plus some additional data whose format is defined by ErrorType. Buffer is allocated by this function with AllocatePool(), and it is the caller's responsibility to free it with a call to FreePool(). @retval EFI_SUCCESS The controller specified by ControllerHandle and ChildHandle passed the diagnostic. @retval EFI_ACCESS_DENIED The request for initiating diagnostics was unable to be complete due to some underlying hardware or software state. @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. @retval EFI_INVALID_PARAMETER Language is NULL. @retval EFI_INVALID_PARAMETER ErrorType is NULL. @retval EFI_INVALID_PARAMETER BufferType is NULL. @retval EFI_INVALID_PARAMETER Buffer is NULL. @retval EFI_UNSUPPORTED The driver specified by This does not support running diagnostics for the controller specified by ControllerHandle and ChildHandle. @retval EFI_UNSUPPORTED The driver specified by This does not support the type of diagnostic specified by DiagnosticType. @retval EFI_UNSUPPORTED The driver specified by This does not support the language specified by Language. @retval EFI_OUT_OF_RESOURCES There are not enough resources available to complete the diagnostics. @retval EFI_OUT_OF_RESOURCES There are not enough resources available to return the status information in ErrorType, BufferSize, and Buffer. @retval EFI_DEVICE_ERROR The controller specified by ControllerHandle and ChildHandle did not pass the diagnostic. **/ EFI_STATUS EFIAPI NvmDimmDriverDriverDiagnosticsRunDiagnostics ( IN EFI_DRIVER_DIAGNOSTICS2_PROTOCOL *pThis, IN EFI_HANDLE ControllerHandle, IN EFI_HANDLE ChildHandle OPTIONAL, IN EFI_DRIVER_DIAGNOSTIC_TYPE DiagnosticType, IN CHAR8 *pLanguage, OUT EFI_GUID **ppErrorType, OUT UINTN *pBufferSize, OUT CHAR16 **ppBuffer ); #endif /** _DRIVER_DIAGNOSTICS_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Driver/DriverHealth.h000066400000000000000000000012071440615110200242210ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DRIVER_HEALTH_H_ #define _DRIVER_HEALTH_H_ #include #include /** Get Dimm health status @param[in] DimmPid Dimm ID @param[out] pHealthStatus A pointer to the health status that is returned by this function. @retval EFI_SUCCESS Completed without issues @retval EFI_INVALID_PARAMETER pHealthStatus is NULL **/ EFI_STATUS GetDimmHealthStatus ( IN UINT16 DimmPid, OUT EFI_DRIVER_HEALTH_STATUS *pHealthStatus ); extern EFI_GUID gNvmDimmDriverHealthGuid; #endif /** _DRIVER_HEALTH_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Driver/NvmDimmConfig.c000066400000000000000000014105541440615110200243420ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include "NvmDimmConfig.h" #ifndef OS_BUILD #include #endif #include "NvmTypes.h" #include #include #include #include #include #include #include #include #include #include "DriverHealth.h" #include #include #include "NvmSecurity.h" #include #include #include #include #include #include #include #include #ifndef OS_BUILD #include #include #include #include #endif #ifdef OS_BUILD #include #include #endif // OS_BUILD #include #define INVALID_SOCKET_ID 0xFFFF #define ARS_LIST_NOT_INITIALIZED -1 #define PMTT_INFO_SIGNATURE SIGNATURE_64('P', 'M', 'T', 'T', 'I', 'N', 'F', 'O') #define PMTT_INFO_FROM_NODE(a) CR(a, PMTT_INFO, PmttNode, PMTT_INFO_SIGNATURE) #define IS_DIMM_SECURITY_ENABLED(SecurityStateBitmask) (SecurityStateBitmask & SECURITY_MASK_ENABLED) /** PMTT 0.2 vendor specific modules: Die, Channel, Slot added **/ typedef struct _PMTT_VERSION { ACPI_REVISION Revision; //!< PMTT Version struct { UINT16 DieID; //!< die identifier UINT16 ChannelID; //!< Channel identifier UINT16 SlotID; //!< Slot identifier } VendorData; } PMTT_VERSION; typedef struct _PMTT_INFO { LIST_ENTRY PmttNode; UINT64 Signature; //!< PMTT_INFO_SIGNATURE PMTT_VERSION PmttVersion; //!< PMTT Version UINT16 DimmID; //!< SMBIOS Type 17 handle corresponding to this memory device UINT16 NodeControllerID; //!< die identifier UINT16 SocketID; //!< zero based socket identifier UINT16 MemControllerID; //!< Memory Controller identifier } PMTT_INFO; typedef struct _REGION_GOAL_APPDIRECT_INDEX_TABLE { REGION_GOAL *pRegionGoal; UINT32 AppDirectIndex; } REGION_GOAL_APPDIRECT_INDEX_TABLE; /** Memory Device SMBIOS Table **/ #define SMBIOS_TYPE_MEM_DEV 17 /** Memory Device Mapped Address SMBIOS Table **/ #define SMBIOS_TYPE_MEM_DEV_MAPPED_ADDR 20 #define TEST_NAMESPACE_NAME_LEN 14 EFI_GUID gNvmDimmConfigProtocolGuid = EFI_DCPMM_CONFIG2_PROTOCOL_GUID; EFI_GUID gNvmDimmPbrProtocolGuid = EFI_DCPMM_PBR_PROTOCOL_GUID; extern NVMDIMMDRIVER_DATA *gNvmDimmData; extern CONST UINT64 gSupportedBlockSizes[SUPPORTED_BLOCK_SIZES_COUNT]; // These will be overwritten by SetDefaultProtocolAndPayloadSizeOptions() EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS gTransportAttribs = { FisTransportAuto, FisTransportSizeAuto }; #ifndef OS_BUILD DCPMM_ARS_ERROR_RECORD * gArsBadRecords = NULL; INT32 gArsBadRecordsCount = ARS_LIST_NOT_INITIALIZED; extern volatile UINT32 _gPcd_BinaryPatch_PcdDebugPrintErrorLevel; #endif /** Instance of NvmDimmConfigProtocol **/ EFI_DCPMM_CONFIG2_PROTOCOL gNvmDimmDriverNvmDimmConfig = { NVMD_CONFIG_PROTOCOL_VERSION, GetDimmCount, GetDimms, GetDimm, #ifdef OS_BUILD GetPMONRegisters, SetPMONRegisters, #endif GetUninitializedDimmCount, GetUninitializedDimms, GetSockets, GetDimmSmbiosTable, GetAcpiNFit, GetAcpiPcat, GetAcpiPMTT, GetPcd, GetSecurityState, SetSecurityState, UpdateFw, GetRegionCount, GetRegions, GetRegion, GetMemoryResourcesInfo, GetDimmsPerformanceData, GetSystemCapabilitiesInfo, GetDriverApiVersion, GetAlarmThresholds, SetAlarmThresholds, GetSmartAndHealth, GetGoalConfigs, GetActualRegionsGoalCapacities, CreateGoalConfig, DeleteGoalConfig, DumpGoalConfig, LoadGoalConfig, StartDiagnostic, CreateNamespace, GetNamespaces, DeleteNamespace, GetErrorLog, GetFwDebugLog, SetOptionalConfigurationDataPolicy, RetrieveDimmRegisters, GetSystemTopology, GetARSStatus, GetDriverPreferences, SetDriverPreferences, DimmFormat, GetDdrtIoInitInfo, GetLongOpStatus, InjectError, GetBSRAndBootStatusBitMask, PassThruCommand, ModifyPcdConfig, GetFisTransportAttributes, SetFisTransportAttributes, GetCommandAccessPolicy, GetCommandEffectLog, #ifndef OS_BUILD GetDriverDebugPrintErrorLevel, SetDriverDebugPrintErrorLevel, #endif //OS_BUILD GetFIPSMode, }; EFI_DCPMM_PBR_PROTOCOL gNvmDimmDriverNvmDimmPbr = { PbrSetMode, PbrGetMode, PbrSetSession, PbrGetSession, PbrFreeSession, PbrResetSession, PbrSetTag, PbrGetTagCount, PbrGetTag, PbrGetDataPlaybackInfo, PbrGetData, PbrSetData, }; /** Retrieve the User Driver Preferences from RunTime Services. @param[out] pDriverPreferences pointer to the current driver preferences. @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS ReadRunTimeDriverPreferences( OUT DRIVER_PREFERENCES *pDriverPreferences ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINTN VariableSize = 0; NVDIMM_ENTRY(); /* check input parameters */ if (pDriverPreferences == NULL) { NVDIMM_DBG("One or more parameters are NULL"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ZeroMem(pDriverPreferences, sizeof(*pDriverPreferences)); VariableSize = sizeof(pDriverPreferences->ChannelInterleaving); ReturnCode = GET_VARIABLE( CHANNEL_INTERLEAVE_SIZE_VARIABLE_NAME, gNvmDimmNgnvmVariableGuid, &VariableSize, &pDriverPreferences->ChannelInterleaving); if(ReturnCode == EFI_NOT_FOUND) { pDriverPreferences->ChannelInterleaving = DEFAULT_CHANNEL_INTERLEAVE_SIZE; ReturnCode = EFI_SUCCESS; } else if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to retrieve Channel Interleave Variable"); goto Finish; } VariableSize = sizeof(pDriverPreferences->ImcInterleaving); ReturnCode = GET_VARIABLE( IMC_INTERLEAVE_SIZE_VARIABLE_NAME, gNvmDimmNgnvmVariableGuid, &VariableSize, &pDriverPreferences->ImcInterleaving); if(ReturnCode == EFI_NOT_FOUND) { pDriverPreferences->ImcInterleaving = DEFAULT_IMC_INTERLEAVE_SIZE; ReturnCode = EFI_SUCCESS; } else if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to retrieve iMC Interleave Variable"); goto Finish; } if ((pDriverPreferences->ImcInterleaving == DEFAULT_IMC_INTERLEAVE_SIZE) != (pDriverPreferences->ChannelInterleaving == DEFAULT_CHANNEL_INTERLEAVE_SIZE)) { NVDIMM_DBG("Only one interleave preference set to default, invalid configuration"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pDriverPreferences->AppDirectGranularity = APPDIRECT_GRANULARITY_DEFAULT; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get information about support of SKU features @param[in] pDimm - pointer to DIMM with SKU informations in bit field form received from FW @param[in] SkuType - feature type @retval TRUE - if feature is supported by current SKU @retval FALSE - if feature is not supported by current SKU **/ STATIC BOOLEAN IsDimmSkuSupported( IN DIMM *pDimm, IN UINT8 SkuType ) { BOOLEAN ReturnValue = FALSE; if (pDimm == NULL) { NVDIMM_DBG("NULL pointer provided."); goto Finish; } switch (SkuType) { case SkuMemoryModeOnly: if (pDimm->SkuInformation.MemoryModeEnabled == MODE_ENABLED && pDimm->SkuInformation.AppDirectModeEnabled == MODE_DISABLED) { ReturnValue = TRUE; } break; case SkuAppDirectModeOnly: if (pDimm->SkuInformation.MemoryModeEnabled == MODE_DISABLED && pDimm->SkuInformation.AppDirectModeEnabled == MODE_ENABLED) { ReturnValue = TRUE; } break; case SkuTriMode: if (pDimm->SkuInformation.MemoryModeEnabled == MODE_ENABLED && pDimm->SkuInformation.AppDirectModeEnabled == MODE_ENABLED) { ReturnValue = TRUE; } break; case SkuPackageSparingCapable: if (pDimm->SkuInformation.PackageSparingCapable == MODE_ENABLED) { ReturnValue = TRUE; } break; case SkuSoftProgrammableSku: if (pDimm->SkuInformation.SoftProgrammableSku == MODE_ENABLED) { ReturnValue = TRUE; } break; case SkuStandardSecuritySku: if (pDimm->SkuInformation.EncryptionEnabled == MODE_ENABLED) { ReturnValue = TRUE; } break; case SkuControlledCountrySku: if (pDimm->SkuInformation.EncryptionEnabled == MODE_DISABLED) { ReturnValue = TRUE; } break; } Finish: return ReturnValue; } /** Retrieve the number of functional DCPMMs in the system found in NFIT @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pDimmCount The number of DCPMMs found in NFIT. @retval EFI_SUCCESS The count was returned properly @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ EFI_STATUS EFIAPI GetDimmCount( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pDimmCount ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pCurrentDimm = NULL; LIST_ENTRY *pCurrentDimmNode = NULL; NVDIMM_ENTRY(); /* check input parameters */ if (pThis == NULL || pDimmCount == NULL) { NVDIMM_DBG("Input parameter is NULL"); goto Finish; } *pDimmCount = 0; LIST_FOR_EACH(pCurrentDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pCurrentDimm = DIMM_FROM_NODE(pCurrentDimmNode); if (TRUE == pCurrentDimm->NonFunctional) { continue; } (*pDimmCount)++; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve the number of non-functional DCPMMs in the system @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pDimmCount The number of DCPMMs. @retval EFI_SUCCESS The count was returned properly @retval EFI_INVALID_PARAMETER One or more parameters are NULL **/ EFI_STATUS EFIAPI GetUninitializedDimmCount( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pDimmCount ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pCurrentDimm = NULL; LIST_ENTRY *pCurrentDimmNode = NULL; NVDIMM_ENTRY(); if (pThis == NULL || pDimmCount == NULL) { NVDIMM_DBG("Input parameter is NULL"); goto Finish; } *pDimmCount = 0; LIST_FOR_EACH(pCurrentDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pCurrentDimm = DIMM_FROM_NODE(pCurrentDimmNode); if (FALSE == pCurrentDimm->NonFunctional) { continue; } (*pDimmCount)++; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #ifdef OS_BUILD /** Read the DIMM's PCD and get the mapped memory sizes @param[in] DimmPid The ID of the DIMM @retval EFI_INVALID_PARAMETER passed NULL argument @retval Other errors failure of FW commands @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI GetDimmMappedMemSize( IN DIMM *pDimm) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_CONFIGURATION_HEADER *pPcdConfHeader = NULL; NVDIMM_CURRENT_CONFIG *pPcdCurrentConf = NULL; if (NULL == pDimm) { return EFI_INVALID_PARAMETER; } if (!IsDimmManageable(pDimm)) { return EFI_NOT_READY; } // DIMM PCD already read if (pDimm->PcdMappedMemInfoRead) { NVDIMM_DBG("DIMM: 0x%04x PCD already read!", pDimm->DeviceHandle.AsUint32); return EFI_SUCCESS; } ReturnCode = GetPlatformConfigDataOemPartition(pDimm, FALSE, &pPcdConfHeader); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_DEVICE_ERROR; goto Finish; } if (pPcdConfHeader->CurrentConfStartOffset == 0 || pPcdConfHeader->CurrentConfDataSize == 0) { NVDIMM_DBG("There is no Current Config table"); ReturnCode = EFI_LOAD_ERROR; goto Finish; } pPcdCurrentConf = GET_NVDIMM_CURRENT_CONFIG(pPcdConfHeader); if (!IsPcdCurrentConfHeaderValid(pPcdCurrentConf, pDimm->PcdOemPartitionSize)) { ReturnCode = EFI_VOLUME_CORRUPTED; goto Finish; } pDimm->ConfigStatus = (UINT8)pPcdCurrentConf->ConfigStatus; pDimm->IsNew = (pDimm->ConfigStatus == DIMM_CONFIG_NEW_DIMM) ? 1 : 0; switch (pPcdCurrentConf->ConfigStatus) { case DIMM_CONFIG_SUCCESS: case DIMM_CONFIG_OLD_CONFIG_USED: // 2LM is not mapped because of NM:FM violation, but 1LM is mapped/healthy case DIMM_CONFIG_DCPMM_NM_FM_RATIO_UNSUPPORTED: case DIMM_CONFIG_PM_MAPPED_VM_POPULATION_ISSUE: pDimm->Configured = TRUE; break; default: pDimm->Configured = FALSE; break; } pDimm->MappedVolatileCapacity = pPcdCurrentConf->VolatileMemSizeIntoSpa; pDimm->MappedPersistentCapacity = pPcdCurrentConf->PersistentMemSizeIntoSpa; pDimm->PcdMappedMemInfoRead = TRUE; ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pPcdConfHeader); return ReturnCode; } #endif // OS_BUILD /* * Helper function for initializing information from the NFIT for non-functional * dimms only. This should eventually include functional dimms as well * (GetDimmInfo), but currently avoiding as it is hard to extract the NFIT-only * calls from GetDimmInfo. */ VOID InitializeNfitDimmInfoFieldsFromDimm( IN DIMM *pDimm, OUT DIMM_INFO *pDimmInfo ) { //pDimm->Signature = DIMM_SIGNATURE; pDimmInfo->Configured = pDimm->Configured; //pDimm->ISsNum = 0; pDimmInfo->DimmID = pDimm->DimmID; pDimmInfo->DimmHandle = pDimm->DeviceHandle.AsUint32; pDimmInfo->SocketId = pDimm->SocketId; pDimmInfo->ImcId = pDimm->ImcId; pDimmInfo->NodeControllerID = pDimm->NodeControllerID; pDimmInfo->ChannelId = pDimm->ChannelId; pDimmInfo->ChannelPos = pDimm->ChannelPos; //pDimm->NvDimmStateFlags pDimmInfo->VendorId = pDimm->VendorId; pDimmInfo->DeviceId = pDimm->DeviceId; pDimmInfo->Rid = pDimm->Rid; pDimmInfo->SubsystemVendorId = pDimm->SubsystemVendorId; pDimmInfo->SubsystemDeviceId = pDimm->SubsystemDeviceId; pDimmInfo->SubsystemRid = pDimm->SubsystemRid; pDimmInfo->ManufacturingInfoValid = pDimm->ManufacturingInfoValid; pDimmInfo->ManufacturingLocation = pDimm->ManufacturingLocation; pDimmInfo->ManufacturingDate = pDimm->ManufacturingDate; pDimmInfo->SerialNumber = pDimm->SerialNumber; pDimmInfo->Capacity = pDimm->RawCapacity; pDimmInfo->ManufacturerId = pDimm->Manufacturer; pDimmInfo->SmbusAddress = pDimm->SmbusAddress; CHECK_RESULT_CONTINUE(GetDimmUid(pDimm, pDimmInfo->DimmUid, MAX_DIMM_UID_LENGTH)); if (StrLen(pDimmInfo->DimmUid) == 0) { pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_UID; } } /* * Helper function for initializing information from the SMBIOS */ EFI_STATUS FillSmbiosInfo( IN OUT DIMM_INFO *pDimmInfo ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TempReturnCode = EFI_SUCCESS; SMBIOS_STRUCTURE_POINTER DmiPhysicalDev; SMBIOS_STRUCTURE_POINTER DmiDeviceMappedAddr; SMBIOS_VERSION SmbiosVersion; UINT64 CapacityFromSmbios = 0; ReturnCode = GetDmiMemdevInfo(pDimmInfo->DimmID, &DmiPhysicalDev, &DmiDeviceMappedAddr, &SmbiosVersion); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failure to retrieve SMBIOS tables"); return ReturnCode; } /* SMBIOS type 17 table info */ if (DmiPhysicalDev.Type17 != NULL) { if (DmiPhysicalDev.Type17->MemoryType == MemoryTypeDdr4) { //Prior to SMBIOS MemoryType 0x1F (Logical non-volatile), DCPM's were identified by //MemoryType 0x1A (DDR4) with TypeDetail[Nonvolatile] set. //Leaving here for backwards compatibility if (DmiPhysicalDev.Type17->TypeDetail.Nonvolatile) { pDimmInfo->MemoryType = MEMORYTYPE_DCPM; } else { pDimmInfo->MemoryType = MEMORYTYPE_DDR4; } } else if (DmiPhysicalDev.Type17->MemoryType == MemoryTypeDdr5) { pDimmInfo->MemoryType = MEMORYTYPE_DDR5; } else if (DmiPhysicalDev.Type17->MemoryType == MemoryTypeLogicalNonVolatileDevice) { pDimmInfo->MemoryType = MEMORYTYPE_DCPM; } else { pDimmInfo->MemoryType = MEMORYTYPE_UNKNOWN; } pDimmInfo->FormFactor = DmiPhysicalDev.Type17->FormFactor; pDimmInfo->DataWidth = DmiPhysicalDev.Type17->DataWidth; pDimmInfo->TotalWidth = DmiPhysicalDev.Type17->TotalWidth; pDimmInfo->Speed = DmiPhysicalDev.Type17->Speed; ReturnCode = GetSmbiosCapacity(DmiPhysicalDev.Type17->Size, DmiPhysicalDev.Type17->ExtendedSize, SmbiosVersion, &CapacityFromSmbios); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to retrieve capacity from SMBIOS table (" FORMAT_EFI_STATUS ")", ReturnCode); } pDimmInfo->CapacityFromSmbios = CapacityFromSmbios; TempReturnCode = GetSmbiosString((SMBIOS_STRUCTURE_POINTER *) &(DmiPhysicalDev.Type17), DmiPhysicalDev.Type17->DeviceLocator, pDimmInfo->DeviceLocator, DEVICE_LOCATOR_LEN); if (EFI_ERROR(TempReturnCode)) { StrnCpyS(pDimmInfo->DeviceLocator, DEVICE_LOCATOR_LEN, SMBIOS_STR_UNKNOWN, StrLen(SMBIOS_STR_UNKNOWN)); NVDIMM_WARN("Failed to retrieve the device locator from SMBIOS table (" FORMAT_EFI_STATUS ")", ReturnCode); } TempReturnCode = GetSmbiosString((SMBIOS_STRUCTURE_POINTER *) &(DmiPhysicalDev.Type17), DmiPhysicalDev.Type17->BankLocator, pDimmInfo->BankLabel, BANKLABEL_LEN); if (EFI_ERROR(TempReturnCode)) { StrnCpyS(pDimmInfo->BankLabel, BANKLABEL_LEN, SMBIOS_STR_UNKNOWN, StrLen(SMBIOS_STR_UNKNOWN)); NVDIMM_WARN("Failed to retrieve the bank locator from SMBIOS table (" FORMAT_EFI_STATUS ")", ReturnCode); } TempReturnCode = GetSmbiosString((SMBIOS_STRUCTURE_POINTER *) &(DmiPhysicalDev.Type17), DmiPhysicalDev.Type17->Manufacturer, pDimmInfo->ManufacturerStr, MANUFACTURER_LEN); if (EFI_ERROR(TempReturnCode)) { StrnCpyS(pDimmInfo->ManufacturerStr, MANUFACTURER_LEN, SMBIOS_STR_UNKNOWN, StrLen(SMBIOS_STR_UNKNOWN)); NVDIMM_WARN("Failed to retrieve the manufacturer string from SMBIOS table (" FORMAT_EFI_STATUS ")", ReturnCode); } } else { NVDIMM_ERR("SMBIOS table of type 17 for DIMM 0x%x was not found.", pDimmInfo->DimmID); } return ReturnCode; } /** Init DIMM_INFO structure for given Initialized DIMM @param[in] pDimm DIMM that will be used to create DIMM_INFO @param[in] dimmInfoCategories DIMM_INFO_CATEGORIES specifies which (if any) additional FW api calls is desired. If DIMM_INFO_CATEGORY_NONE, then only the properties from the pDimm struct will be populated. @param[in,out] pDimmInfo DIMM_INFO instance to fill in @retval EFI_SUCCESS Creation performed without errors @retval EFI_INVALID_PARAMETER If pDimm or pDimmMinInfo is NULL @retval EFI_DEVICE_ERROR If communication with DIMM fails **/ EFI_STATUS GetDimmInfo ( IN DIMM *pDimm, IN DIMM_INFO_CATEGORIES dimmInfoCategories, IN OUT DIMM_INFO *pDimmInfo ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PT_GET_SECURITY_PAYLOAD *pSecurityPayload = NULL; PT_OUTPUT_PAYLOAD_GET_SECURITY_OPT_IN *pSecurityOptInPayload = NULL; PT_PAYLOAD_GET_PACKAGE_SPARING_POLICY *pGetPackageSparingPayload = NULL; LIST_ENTRY *pNodeNamespace = NULL; NAMESPACE *pCurNamespace = NULL; SMART_AND_HEALTH_INFO HealthInfo; PT_OPTIONAL_DATA_POLICY_PAYLOAD OptionalDataPolicyPayload; PT_VIRAL_POLICY_PAYLOAD ViralPolicyPayload; PT_POWER_MANAGEMENT_POLICY_OUT *pPowerManagementPolicyPayload = NULL; PT_DEVICE_CHARACTERISTICS_OUT *pDevCharacteristics = NULL; PT_OUTPUT_PAYLOAD_MEMORY_INFO_PAGE3 *pPayloadMemInfoPage3 = NULL; PT_OUTPUT_PAYLOAD_MEMORY_INFO_PAGE4 *pPayloadMemInfoPage4 = NULL; PT_PAYLOAD_FW_IMAGE_INFO *pPayloadFwImage = NULL; PT_OUTPUT_PAYLOAD_GET_EADR PayloadExtendedAdr; PT_OUTPUT_PAYLOAD_GET_LATCH_SYSTEM_SHUTDOWN_STATE PayloadLatchSystemShutdownState; SMBIOS_STRUCTURE_POINTER DmiPhysicalDev; SMBIOS_STRUCTURE_POINTER DmiDeviceMappedAddr; SMBIOS_VERSION SmbiosVersion; UINT32 Index = 0; NVDIMM_ENTRY(); ZeroMem(&HealthInfo, sizeof(HealthInfo)); ZeroMem(&OptionalDataPolicyPayload, sizeof(OptionalDataPolicyPayload)); ZeroMem(&ViralPolicyPayload, sizeof(ViralPolicyPayload)); ZeroMem(&DmiPhysicalDev, sizeof(DmiPhysicalDev)); ZeroMem(&DmiDeviceMappedAddr, sizeof(DmiDeviceMappedAddr)); ZeroMem(&SmbiosVersion, sizeof(SmbiosVersion)); if (pDimm == NULL || pDimmInfo == NULL) { NVDIMM_DBG("Convert operation failed. Invalid pointer"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } #ifdef OS_BUILD GetDimmMappedMemSize(pDimm); #endif // OS_BUILD pDimmInfo->DimmID = pDimm->DimmID; pDimmInfo->SocketId = pDimm->SocketId; pDimmInfo->ChannelId = pDimm->ChannelId; pDimmInfo->ChannelPos = pDimm->ChannelPos; for (Index = 0; Index < pDimm->FmtInterfaceCodeNum; Index++) { pDimmInfo->InterfaceFormatCode[Index] = pDimm->FmtInterfaceCode[Index]; } pDimmInfo->InterfaceFormatCodeNum = pDimm->FmtInterfaceCodeNum; pDimmInfo->DimmHandle = pDimm->DeviceHandle.AsUint32; pDimmInfo->FwVer = pDimm->FwVer; pDimmInfo->FwActiveApiVersionMajor = pDimm->FwActiveApiVersionMajor; pDimmInfo->FwActiveApiVersionMinor = pDimm->FwActiveApiVersionMinor; /* Package Sparing Capable */ if (pDimm->SkuInformation.PackageSparingCapable) { pDimmInfo->PackageSparingCapable = TRUE; } else { pDimmInfo->PackageSparingCapable = FALSE; } /* Memory Modes Supported */ pDimmInfo->ModesSupported = 0; if (pDimm->SkuInformation.MemoryModeEnabled == MODE_ENABLED) { pDimmInfo->ModesSupported |= BIT0; } if (pDimm->SkuInformation.AppDirectModeEnabled == MODE_ENABLED) { pDimmInfo->ModesSupported |= BIT2; } /* Security Capabilities */ if (pDimm->SkuInformation.EncryptionEnabled == MODE_ENABLED) { pDimmInfo->SecurityCapabilities |= (SECURITY_CAPABILITIES_ERASE_CAPABLE | SECURITY_CAPABILITIES_ENCRYPTION_SUPPORTED); } pDimmInfo->VendorId = pDimm->VendorId; pDimmInfo->DeviceId = pDimm->DeviceId; pDimmInfo->Rid = pDimm->Rid; pDimmInfo->ImcId = pDimm->ImcId; pDimmInfo->NodeControllerID = pDimm->NodeControllerID; pDimmInfo->SubsystemVendorId = pDimm->SubsystemVendorId; pDimmInfo->SubsystemDeviceId = pDimm->SubsystemDeviceId; pDimmInfo->SubsystemRid = pDimm->SubsystemRid; pDimmInfo->ControllerRid = pDimm->ControllerRid; pDimmInfo->ManufacturingInfoValid = pDimm->ManufacturingInfoValid; pDimmInfo->ManufacturingLocation = pDimm->ManufacturingLocation; pDimmInfo->ManufacturingDate = pDimm->ManufacturingDate; pDimmInfo->ManufacturerId = pDimm->Manufacturer; pDimmInfo->SerialNumber = pDimm->SerialNumber; ReturnCode = FillSmbiosInfo(pDimmInfo); CHECK_RESULT_CONTINUE(GetDimmUid(pDimm, pDimmInfo->DimmUid, MAX_DIMM_UID_LENGTH)); if (StrLen(pDimmInfo->DimmUid) == 0) { pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_UID; } ReturnCode = GetDimmUid(pDimm, pDimmInfo->DimmUid, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (StrLen(pDimmInfo->DimmUid) == 0) { pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_UID; } // Set defaults first pDimmInfo->ManageabilityState = MANAGEMENT_VALID_CONFIG; pDimmInfo->HealthState = HEALTH_UNKNOWN; // Then change as needed. if (!IsDimmManageable(pDimm)) { pDimmInfo->ManageabilityState = MANAGEMENT_INVALID_CONFIG; pDimmInfo->HealthState = HEALTH_UNMANAGEABLE; } else if (TRUE == pDimm->NonFunctional) { pDimmInfo->HealthState = HEALTH_NON_FUNCTIONAL; } pDimmInfo->IsNew = pDimm->IsNew; pDimmInfo->RebootNeeded = pDimm->RebootNeeded; pDimmInfo->PmCapacity = pDimm->PmCapacity; /* Configuration Status */ switch (pDimm->ConfigStatus) { case DIMM_CONFIG_SUCCESS: pDimmInfo->ConfigStatus = DIMM_INFO_CONFIG_VALID; break; case DIMM_CONFIG_OLD_CONFIG_USED: pDimmInfo->ConfigStatus = DIMM_INFO_CONFIG_REVERTED; break; case DIMM_CONFIG_REVISION_NOT_SUPPORTED: pDimmInfo->ConfigStatus = DIMM_INFO_CONFIG_UNSUPPORTED; break; case DIMM_CONFIG_UNDEFINED: case DIMM_CONFIG_RESERVED: case DIMM_CONFIG_NEW_DIMM: pDimmInfo->ConfigStatus = DIMM_INFO_CONFIG_NOT_CONFIG; break; case DIMM_CONFIG_IS_INCOMPLETE: case DIMM_CONFIG_NO_MATCHING_IS: pDimmInfo->ConfigStatus = DIMM_INFO_CONFIG_BROKEN_INTERLEAVE; break; case DIMM_CONFIG_DCPMM_POPULATION_ISSUE: if ((pDimm->ISsNum > 0) && (pDimm->NvDimmStateFlags & NVDIMM_STATE_FLAGS_NOT_MAPPED)) { pDimmInfo->ConfigStatus = DIMM_INFO_CONFIG_BROKEN_INTERLEAVE; } else { pDimmInfo->ConfigStatus = DIMM_INFO_CONFIG_UNSUPPORTED; } break; // 2LM is not mapped because of NM:FM violation, but 1LM is mapped/healthy case DIMM_CONFIG_DCPMM_NM_FM_RATIO_UNSUPPORTED: case DIMM_CONFIG_PM_MAPPED_VM_POPULATION_ISSUE: pDimmInfo->ConfigStatus = DIMM_INFO_CONFIG_PARTIALLY_SUPPORTED; break; case DIMM_CONFIG_BAD_CONFIG: case DIMM_CONFIG_IN_CHECKSUM_NOT_VALID: case DIMM_CONFIG_CURR_CHECKSUM_NOT_VALID: case DIMM_CONFIG_PM_NOT_MAPPED: case DIMM_CONFIG_CPU_MAX_MEMORY_LIMIT_VIOLATION: pDimmInfo->ConfigStatus = DIMM_INFO_CONFIG_BAD_CONFIG; break; default: pDimmInfo->ConfigStatus = DIMM_INFO_CONFIG_NOT_CONFIG; break; } pDimmInfo->IsInPopulationViolation = IsDimmInPopulationViolation(pDimm); pDimmInfo->SkuInformation = *((UINT32 *) &pDimm->SkuInformation); /* SKU Violation */ pDimmInfo->SKUViolation = FALSE; if (pDimm->MappedVolatileCapacity > 0 && pDimm->SkuInformation.MemoryModeEnabled == MODE_DISABLED) { pDimmInfo->SKUViolation = TRUE; } else if (pDimm->MappedPersistentCapacity > 0 && pDimm->SkuInformation.AppDirectModeEnabled == MODE_DISABLED) { pDimmInfo->SKUViolation = TRUE; } else { for (Index = 0; Index < pDimm->ISsNum; Index++) { LIST_FOR_EACH(pNodeNamespace, &pDimm->pISs[Index]->AppDirectNamespaceList) { pCurNamespace = NAMESPACE_FROM_NODE(pNodeNamespace, IsNode); if (pCurNamespace->NamespaceType == APPDIRECT_NAMESPACE && pDimm->SkuInformation.AppDirectModeEnabled == MODE_DISABLED) { pDimmInfo->SKUViolation = TRUE; break; } } } } AsciiStrToUnicodeStrS(pDimm->PartNumber, pDimmInfo->PartNumber, PART_NUMBER_LEN + 1); if (dimmInfoCategories & DIMM_INFO_CATEGORY_SECURITY) { /* Security opt-in */ pSecurityOptInPayload = AllocateZeroPool(sizeof(*pSecurityOptInPayload)); if (pSecurityOptInPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } /* Get Security Opt-In SVN Downgrade*/ pDimmInfo->SVNDowngradeOptIn = OPT_IN_VALUE_INVALID; ReturnCode = FwCmdGetSecurityOptIn(pDimm, NVM_SVN_DOWNGRADE, pSecurityOptInPayload); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FW CMD Error (OPT_IN_SVN_DOWNGRADE): " FORMAT_EFI_STATUS "", ReturnCode); pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_SVN_DOWNGRADE; } if (pSecurityOptInPayload->OptInCode == NVM_SVN_DOWNGRADE) { pDimmInfo->SVNDowngradeOptIn = pSecurityOptInPayload->OptInValue; } pSecurityOptInPayload = ZeroMem(pSecurityOptInPayload, sizeof(*pSecurityOptInPayload)); /* Get Security Opt-In Secure Erase Policy*/ pDimmInfo->SecureErasePolicyOptIn = OPT_IN_VALUE_INVALID; ReturnCode = FwCmdGetSecurityOptIn(pDimm, NVM_SECURE_ERASE_POLICY, pSecurityOptInPayload); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FW CMD Error (OPT_IN_SECURE_ERASE_POLICY): " FORMAT_EFI_STATUS "", ReturnCode); pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_SECURE_ERASE_POLICY; } if (pSecurityOptInPayload->OptInCode == NVM_SECURE_ERASE_POLICY) { pDimmInfo->SecureErasePolicyOptIn = pSecurityOptInPayload->OptInValue; } pSecurityOptInPayload = ZeroMem(pSecurityOptInPayload, sizeof(*pSecurityOptInPayload)); /* Get Security Opt-In S3 Resume */ pDimmInfo->S3ResumeOptIn = OPT_IN_VALUE_INVALID; ReturnCode = FwCmdGetSecurityOptIn(pDimm, NVM_S3_RESUME, pSecurityOptInPayload); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FW CMD Error (OPT_IN_S3_RESUME): " FORMAT_EFI_STATUS "", ReturnCode); pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_S3RESUME; } if (pSecurityOptInPayload->OptInCode == NVM_S3_RESUME) { pDimmInfo->S3ResumeOptIn = pSecurityOptInPayload->OptInValue; } pSecurityOptInPayload = ZeroMem(pSecurityOptInPayload, sizeof(*pSecurityOptInPayload)); /* Get FW Activate Opt-In Secure Erase Policy*/ pDimmInfo->FwActivateOptIn = OPT_IN_VALUE_INVALID; ReturnCode = FwCmdGetSecurityOptIn(pDimm, NVM_FW_ACTIVATE, pSecurityOptInPayload); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FW CMD Error (OPT_IN_FW_ACTIVATE): " FORMAT_EFI_STATUS "", ReturnCode); pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_FW_ACTIVATE; } if (pSecurityOptInPayload->OptInCode == NVM_FW_ACTIVATE) { pDimmInfo->FwActivateOptIn = pSecurityOptInPayload->OptInValue; } /* security state */ pSecurityPayload = AllocateZeroPool(sizeof(*pSecurityPayload)); if (pSecurityPayload == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = FwCmdGetSecurityInfo(pDimm, pSecurityPayload); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FW CMD Error (SECURITY_INFO): " FORMAT_EFI_STATUS "", ReturnCode); pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_SECURITY_INFO; } pDimmInfo->SecurityStateBitmask = pSecurityPayload->SecurityStatus.AsUint32; ConvertSecurityBitmask(pSecurityPayload->SecurityStatus.AsUint32, &pDimmInfo->SecurityState); pDimmInfo->MasterPassphraseEnabled = (BOOLEAN) pSecurityPayload->SecurityStatus.Separated.MasterPassphraseEnabled; } if (dimmInfoCategories & DIMM_INFO_CATEGORY_PACKAGE_SPARING) { ReturnCode = FwCmdGetPackageSparingPolicy(pDimm, &pGetPackageSparingPayload); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Get package sparing policy failed with error " FORMAT_EFI_STATUS " for DIMM 0x%x", ReturnCode, pDimm->DeviceHandle.AsUint32); pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_PACKAGE_SPARING; } else { pDimmInfo->PackageSparingEnabled = pGetPackageSparingPayload->Enable; // This maintains backwards compatibility with FIS 1.3 pDimmInfo->PackageSparesAvailable = (pGetPackageSparingPayload->Supported > PACKAGE_SPARING_NOT_SUPPORTED) ? PACKAGE_SPARES_AVAILABLE : PACKAGE_SPARES_NOT_AVAILABLE; } } if (dimmInfoCategories & DIMM_INFO_CATEGORY_ARS_STATUS) { /* address range scrub */ ReturnCode = FwCmdGetARS(pDimm, &pDimmInfo->ARSStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FwCmdGetARS failed with error " FORMAT_EFI_STATUS " for DIMM %d", ReturnCode, pDimm->DeviceHandle.AsUint32); } } if (dimmInfoCategories & DIMM_INFO_CATEGORY_SMART_AND_HEALTH) { /* Get current health state */ ReturnCode = GetSmartAndHealth(&gNvmDimmDriverNvmDimmConfig,pDimm->DimmID, &HealthInfo); if (EFI_ERROR(ReturnCode)) { pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_SMART_AND_HEALTH; } // Fill in the DCPMM's understanding of its own HealthState if we didn't have any // other opinions earlier if (HEALTH_UNKNOWN == pDimmInfo->HealthState) { ConvertHealthBitmask(HealthInfo.HealthStatus, &pDimmInfo->HealthState); } pDimmInfo->HealthStatusReason = HealthInfo.HealthStatusReason; pDimmInfo->LatchedLastShutdownStatusDetails = HealthInfo.LatchedLastShutdownStatusDetails; pDimmInfo->UnlatchedLastShutdownStatusDetails = HealthInfo.UnlatchedLastShutdownStatusDetails; pDimmInfo->ThermalThrottlePerformanceLossPrct = HealthInfo.ThermalThrottlePerformanceLossPrct; pDimmInfo->LastShutdownTime = HealthInfo.LastShutdownTime; pDimmInfo->AitDramEnabled = HealthInfo.AitDramEnabled; pDimmInfo->MaxMediaTemperature = HealthInfo.MaxMediaTemperature; pDimmInfo->MaxControllerTemperature = HealthInfo.MaxControllerTemperature; } if (dimmInfoCategories & DIMM_INFO_CATEGORY_POWER_MGMT_POLICY) { /* Get current Power Management Policy info */ ReturnCode = FwCmdGetPowerManagementPolicy(pDimm, &pPowerManagementPolicyPayload); if (ReturnCode == EFI_OUT_OF_RESOURCES) { goto Finish; } else if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FwCmdGetPowerManagementPolicy failed with error " FORMAT_EFI_STATUS " for DIMM 0x%x", ReturnCode, pDimm->DeviceHandle.AsUint32); pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_POWER_MGMT; } else { if (2 > pPowerManagementPolicyPayload->FisMajor) { pDimmInfo->PeakPowerBudget.Header.Status.Code = ReturnCode; pDimmInfo->PeakPowerBudget.Header.Type = DIMM_INFO_TYPE_UINT16; pDimmInfo->PeakPowerBudget.Data = pPowerManagementPolicyPayload->Payload.Fis_1_15.PeakPowerBudget; } else { pDimmInfo->PeakPowerBudget.Header.Status.Code = EFI_UNSUPPORTED; } pDimmInfo->AvgPowerLimit.Header.Status.Code = ReturnCode; pDimmInfo->AvgPowerLimit.Header.Type = DIMM_INFO_TYPE_UINT16; pDimmInfo->AvgPowerLimit.Data = pPowerManagementPolicyPayload->Payload.Fis_2_01.AveragePowerLimit; if (2 <= pPowerManagementPolicyPayload->FisMajor) { /* 2.1+: MemoryBandwidthBoostFeature */ pDimmInfo->MemoryBandwidthBoostFeature.Header.Status.Code = ReturnCode; pDimmInfo->MemoryBandwidthBoostFeature.Header.Type = DIMM_INFO_TYPE_UINT16; pDimmInfo->MemoryBandwidthBoostFeature.Data = pPowerManagementPolicyPayload->Payload.Fis_2_01.MemoryBandwidthBoostFeature; /* 2.1+: MemoryBandwidthBoostMaxPowerLimit */ pDimmInfo->MemoryBandwidthBoostMaxPowerLimit.Header.Status.Code = ReturnCode; pDimmInfo->MemoryBandwidthBoostMaxPowerLimit.Header.Type = DIMM_INFO_TYPE_UINT16; pDimmInfo->MemoryBandwidthBoostMaxPowerLimit.Data = pPowerManagementPolicyPayload->Payload.Fis_2_01.MemoryBandwidthBoostMaxPowerLimit; } else { pDimmInfo->MemoryBandwidthBoostFeature.Header.Status.Code = EFI_UNSUPPORTED; pDimmInfo->MemoryBandwidthBoostMaxPowerLimit.Header.Status.Code = EFI_UNSUPPORTED; } if ((2 <= pPowerManagementPolicyPayload->FisMajor && 1 <= pPowerManagementPolicyPayload->FisMinor) || 3 <= pPowerManagementPolicyPayload->FisMajor) { pDimmInfo->MemoryBandwidthBoostAveragePowerTimeConstant.Header.Status.Code = ReturnCode; pDimmInfo->MemoryBandwidthBoostAveragePowerTimeConstant.Header.Type = DIMM_INFO_TYPE_UINT32; pDimmInfo->MemoryBandwidthBoostAveragePowerTimeConstant.Data = pPowerManagementPolicyPayload->Payload.Fis_2_01.MemoryBandwidthBoostAveragePowerTimeConstant; } else { pDimmInfo->MemoryBandwidthBoostAveragePowerTimeConstant.Header.Status.Code = EFI_UNSUPPORTED; } } } if (dimmInfoCategories & DIMM_INFO_CATEGORY_DEVICE_CHARACTERISTICS) { ReturnCode = FwCmdDeviceCharacteristics(pDimm, &pDevCharacteristics); if (ReturnCode == EFI_OUT_OF_RESOURCES) { goto Finish; } else if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FwCmdDeviceCharacteristics failed with error " FORMAT_EFI_STATUS " for DIMM 0x%x", ReturnCode, pDimm->DeviceHandle.AsUint32); pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_DEVICE_CHARACTERISTICS; } else { /* MaxAveragePowerLimit */ if ((1 <= pDevCharacteristics->FisMajor && 13 <= pDevCharacteristics->FisMinor) || 2 <= pDevCharacteristics->FisMajor) { pDimmInfo->MaxAveragePowerLimit.Header.Status.Code = ReturnCode; pDimmInfo->MaxAveragePowerLimit.Header.Type = DIMM_INFO_TYPE_UINT16; pDimmInfo->MaxAveragePowerLimit.Data = pDevCharacteristics->Payload.Fis_2_01.MaxAveragePowerLimit; } else { pDimmInfo->MaxAveragePowerLimit.Header.Status.Code = EFI_UNSUPPORTED; } /* 2.1+ MaxMemoryBandwidthBoostMaxPowerLimit */ if (2 <= pDevCharacteristics->FisMajor) { pDimmInfo->MaxMemoryBandwidthBoostMaxPowerLimit.Header.Status.Code = ReturnCode; pDimmInfo->MaxMemoryBandwidthBoostMaxPowerLimit.Header.Type = DIMM_INFO_TYPE_UINT16; pDimmInfo->MaxMemoryBandwidthBoostMaxPowerLimit.Data = pDevCharacteristics->Payload.Fis_2_01.MaxMemoryBandwidthBoostMaxPowerLimit; } else { pDimmInfo->MaxMemoryBandwidthBoostMaxPowerLimit.Header.Status.Code = EFI_UNSUPPORTED; } /* 2.1+ MaxMemoryBandwidthBoostAveragePowerTimeConstant MemoryBandwidthBoostAveragePowerTimeConstantStep MaxAveragePowerReportingTimeConstant AveragePowerReportingTimeConstantStep */ if ((2 <= pDevCharacteristics->FisMajor && 1 <= pDevCharacteristics->FisMinor) || 3 <= pDevCharacteristics->FisMajor) { pDimmInfo->MaxMemoryBandwidthBoostAveragePowerTimeConstant.Header.Status.Code = ReturnCode; pDimmInfo->MaxMemoryBandwidthBoostAveragePowerTimeConstant.Header.Type = DIMM_INFO_TYPE_UINT32; pDimmInfo->MaxMemoryBandwidthBoostAveragePowerTimeConstant.Data = pDevCharacteristics->Payload.Fis_2_01.MaxMemoryBandwidthBoostAveragePowerTimeConstant; pDimmInfo->MemoryBandwidthBoostAveragePowerTimeConstantStep.Header.Status.Code = ReturnCode; pDimmInfo->MemoryBandwidthBoostAveragePowerTimeConstantStep.Header.Type = DIMM_INFO_TYPE_UINT32; pDimmInfo->MemoryBandwidthBoostAveragePowerTimeConstantStep.Data = pDevCharacteristics->Payload.Fis_2_01.MemoryBandwidthBoostAveragePowerTimeConstantStep; pDimmInfo->MaxAveragePowerReportingTimeConstant.Header.Status.Code = ReturnCode; pDimmInfo->MaxAveragePowerReportingTimeConstant.Header.Type = DIMM_INFO_TYPE_UINT32; pDimmInfo->MaxAveragePowerReportingTimeConstant.Data = pDevCharacteristics->Payload.Fis_2_01.MaxAveragePowerReportingTimeConstant; pDimmInfo->AveragePowerReportingTimeConstantStep.Header.Status.Code = ReturnCode; pDimmInfo->AveragePowerReportingTimeConstantStep.Header.Type = DIMM_INFO_TYPE_UINT32; pDimmInfo->AveragePowerReportingTimeConstantStep.Data = pDevCharacteristics->Payload.Fis_2_01.AveragePowerReportingTimeConstantStep; } else { pDimmInfo->MaxMemoryBandwidthBoostAveragePowerTimeConstant.Header.Status.Code = EFI_UNSUPPORTED; pDimmInfo->MemoryBandwidthBoostAveragePowerTimeConstantStep.Header.Status.Code = EFI_UNSUPPORTED; pDimmInfo->MaxAveragePowerReportingTimeConstant.Header.Status.Code = EFI_UNSUPPORTED; pDimmInfo->AveragePowerReportingTimeConstantStep.Header.Status.Code = EFI_UNSUPPORTED; } } } if (dimmInfoCategories & DIMM_INFO_CATEGORY_OPTIONAL_CONFIG_DATA_POLICY) { /* Get current AveragePowerReportingTimeConstant */ ReturnCode = FwCmdGetOptionalConfigurationDataPolicy(pDimm, &OptionalDataPolicyPayload); if (EFI_ERROR(ReturnCode)) { pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_OPTIONAL_CONFIG_DATA; } /* AvgPowerReportingTimeConstant */ if ((2 == OptionalDataPolicyPayload.FisMajor && 1 <= OptionalDataPolicyPayload.FisMinor) || 3 <= OptionalDataPolicyPayload.FisMajor) { pDimmInfo->AvgPowerReportingTimeConstant.Header.Status.Code = ReturnCode; pDimmInfo->AvgPowerReportingTimeConstant.Header.Type = DIMM_INFO_TYPE_UINT32; pDimmInfo->AvgPowerReportingTimeConstant.Data = OptionalDataPolicyPayload.Payload.Fis_2_01.AveragePowerReportingTimeConstant; } else { pDimmInfo->AvgPowerReportingTimeConstant.Header.Status.Code = EFI_UNSUPPORTED; } } if (dimmInfoCategories & DIMM_INFO_CATEGORY_VIRAL_POLICY) { /* Get current ViralPolicy state */ ReturnCode = FwCmdGetViralPolicy(pDimm, &ViralPolicyPayload); if (EFI_ERROR(ReturnCode)) { pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_VIRAL_POLICY; } pDimmInfo->ViralPolicyEnable = ViralPolicyPayload.ViralPolicyEnable; pDimmInfo->ViralStatus = ViralPolicyPayload.ViralStatus; } if (dimmInfoCategories & DIMM_INFO_CATEGORY_OVERWRITE_DIMM_STATUS) { ReturnCode = GetOverwriteDimmStatus(pDimm, &pDimmInfo->OverwriteDimmStatus); if (EFI_ERROR(ReturnCode)) { pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_OVERWRITE_STATUS; } } // Data already in pDimm pDimmInfo->Configured = pDimm->Configured; ReturnCode = GetDcpmmCapacities(pDimm->DimmID, &pDimmInfo->Capacity, &pDimmInfo->VolatileCapacity, &pDimmInfo->AppDirectCapacity, &pDimmInfo->UnconfiguredCapacity, &pDimmInfo->ReservedCapacity, &pDimmInfo->InaccessibleCapacity); if (EFI_ERROR(ReturnCode)) { pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_CAPACITY; } if (dimmInfoCategories & DIMM_INFO_CATEGORY_FW_IMAGE_INFO) { ReturnCode = FwCmdGetFirmwareImageInfo(pDimm, &pPayloadFwImage); if (EFI_ERROR(ReturnCode)) { pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_FW_IMAGE_INFO; // maybe used in other caller APIs excluding ShowDimms() } else { pDimmInfo->LastFwUpdateStatus = pPayloadFwImage->LastFwUpdateStatus; pDimmInfo->StagedFwVersion = ParseFwVersion(pPayloadFwImage->StagedFwRevision); pDimmInfo->StagedFwActivatable = pPayloadFwImage->StagedFwActivatable; pDimmInfo->FWImageMaxSize = pPayloadFwImage->FWImageMaxSize * BLOCKSIZE_4K; // the value FWImageMaxSize is actually the multiply of 4 KiB blocks pDimmInfo->QuiesceRequired = pPayloadFwImage->QuiesceRequired; pDimmInfo->ActivationTime = pPayloadFwImage->ActivationTime; } } if (dimmInfoCategories & DIMM_INFO_CATEGORY_MEM_INFO_PAGE_3) { ReturnCode = FwCmdGetMemoryInfoPage(pDimm, MEMORY_INFO_PAGE_3, sizeof(PT_OUTPUT_PAYLOAD_MEMORY_INFO_PAGE3), (VOID **)&pPayloadMemInfoPage3); if (EFI_ERROR(ReturnCode)) { pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_MEM_INFO_PAGE; } else { pDimmInfo->ErrorInjectionEnabled = (pPayloadMemInfoPage3->ErrorInjectStatus >> ERR_INJECTION_ENABLED_BIT) & ERR_INJECTION_ENABLED_BIT_MASK; pDimmInfo->MediaTemperatureInjectionEnabled = (pPayloadMemInfoPage3->ErrorInjectStatus >> ERR_INJECTION_MEDIA_TEMP_ENABLED_BIT) & ERR_INJECTION_MEDIA_TEMP_ENABLED_BIT_MASK; pDimmInfo->SoftwareTriggersEnabled = (pPayloadMemInfoPage3->ErrorInjectStatus >> ERR_INJECTION_SW_TRIGGER_ENABLED_BIT) & ERR_INJECTION_SW_TRIGGER_ENABLED_BIT_MASK; pDimmInfo->PoisonErrorInjectionsCounter = pPayloadMemInfoPage3->PoisonErrorInjectionsCounter; pDimmInfo->PoisonErrorClearCounter = pPayloadMemInfoPage3->PoisonErrorClearCounter; pDimmInfo->MediaTemperatureInjectionsCounter = pPayloadMemInfoPage3->MediaTemperatureInjectionsCounter; pDimmInfo->SoftwareTriggersCounter = pPayloadMemInfoPage3->SoftwareTriggersCounter; pDimmInfo->SoftwareTriggersEnabledDetails = pPayloadMemInfoPage3->SoftwareTriggersEnabledDetails; } } if (dimmInfoCategories & DIMM_INFO_CATEGORY_MEM_INFO_PAGE_4) { ReturnCode = FwCmdGetMemoryInfoPage(pDimm, MEMORY_INFO_PAGE_4, sizeof(PT_OUTPUT_PAYLOAD_MEMORY_INFO_PAGE4), (VOID **)&pPayloadMemInfoPage4); pDimmInfo->DcpmmAveragePower.Header.Status.Code = ReturnCode; if (pDimm->FwVer.FwApiMajor < 3) { pDimmInfo->AveragePower12V.Header.Status.Code = ReturnCode; pDimmInfo->AveragePower1_2V.Header.Status.Code = ReturnCode; } else { pDimmInfo->AveragePower12V.Header.Status.Code = EFI_UNSUPPORTED; pDimmInfo->AveragePower1_2V.Header.Status.Code = EFI_UNSUPPORTED; } if (EFI_SUCCESS == ReturnCode) { pDimmInfo->DcpmmAveragePower.Data = pPayloadMemInfoPage4->DcpmmAveragePower; pDimmInfo->DcpmmAveragePower.Header.Type = DIMM_INFO_TYPE_UINT16; pDimmInfo->AveragePower12V.Data = pPayloadMemInfoPage4->AveragePower12V; pDimmInfo->AveragePower12V.Header.Type = DIMM_INFO_TYPE_UINT16; pDimmInfo->AveragePower1_2V.Data = pPayloadMemInfoPage4->AveragePower1_2V; pDimmInfo->AveragePower1_2V.Header.Type = DIMM_INFO_TYPE_UINT16; } } if (dimmInfoCategories & DIMM_INFO_CATEGORY_EXTENDED_ADR) { ReturnCode = FwCmdGetExtendedAdrInfo(pDimm, &PayloadExtendedAdr); pDimmInfo->ExtendedAdrEnabled.Header.Status.Code = ReturnCode; pDimmInfo->PrevPwrCycleExtendedAdrEnabled.Header.Status.Code = ReturnCode; if (EFI_SUCCESS == ReturnCode) { pDimmInfo->ExtendedAdrEnabled.Data = PayloadExtendedAdr.ExtendedAdrStatus; pDimmInfo->ExtendedAdrEnabled.Header.Type = DIMM_INFO_TYPE_BOOLEAN; pDimmInfo->PrevPwrCycleExtendedAdrEnabled.Data = PayloadExtendedAdr.PreviousExtendedAdrStatus; pDimmInfo->PrevPwrCycleExtendedAdrEnabled.Header.Type = DIMM_INFO_TYPE_BOOLEAN; } } if (dimmInfoCategories & DIMM_INFO_CATEGORY_LATCH_SYSTEM_SHUTDOWN_STATE) { ReturnCode = FwCmdGetLatchSystemShutdownStateInfo(pDimm, &PayloadLatchSystemShutdownState); if (EFI_ERROR(ReturnCode)) { pDimmInfo->ErrorMask |= DIMM_INFO_ERROR_LATCH_SYSTEM_SHUTDOWN_STATE; } pDimmInfo->LatchSystemShutdownState = PayloadLatchSystemShutdownState.LatchSystemShutdownState; pDimmInfo->PrevPwrCycleLatchSystemShutdownState = PayloadLatchSystemShutdownState.PreviousPowerCycleLatchSystemShutdownState; } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pPowerManagementPolicyPayload); FREE_POOL_SAFE(pDevCharacteristics); FREE_POOL_SAFE(pPayloadMemInfoPage3); FREE_POOL_SAFE(pPayloadMemInfoPage4); FREE_POOL_SAFE(pPayloadFwImage); FREE_POOL_SAFE(pGetPackageSparingPayload); FREE_POOL_SAFE(pSecurityOptInPayload); FREE_POOL_SAFE(pSecurityPayload); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check if there is at least one DIMM on specified socket @param[in] SocketId @retval FALSE SocketId is not valid, it is out of allowed range or there was a problem with getting DIMM list **/ STATIC BOOLEAN IsSocketIdValid( IN UINT16 SocketId ) { NVDIMM_ENTRY(); BOOLEAN SocketIdValid = FALSE; DIMM *pDimm = NULL; LIST_ENTRY *pCurrentDimmNode = NULL; // socket id must be within allowed range if (SocketId > MAX_SOCKETS) { goto Finish; } // at least one dimm must be plugged to specified socket LIST_FOR_EACH(pCurrentDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pCurrentDimmNode); if (pDimm != NULL && pDimm->SocketId == SocketId) { SocketIdValid = TRUE; goto Finish; } } Finish: NVDIMM_EXIT(); return SocketIdValid; } /** Check if security option is supported @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] SecurityOperation @retval TRUE Security operation is supported @retval FALSE Security operation is not supported **/ STATIC BOOLEAN IsSecurityOpSupported( IN UINT16 SecurityOperation ) { BOOLEAN Result = FALSE; SYSTEM_CAPABILITIES_INFO SystemCapabilitiesInfo; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); SystemCapabilitiesInfo.PtrInterleaveFormatsSupported = 0; ReturnCode = GetSystemCapabilitiesInfo(&gNvmDimmDriverNvmDimmConfig, &SystemCapabilitiesInfo); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetSystemCapabilitiesInfo"); goto Finish; } switch (SecurityOperation) { case SECURITY_OPERATION_SET_PASSPHRASE: if (SystemCapabilitiesInfo.EnableDeviceSecuritySupported == FEATURE_SUPPORTED) { Result = TRUE; } break; case SECURITY_OPERATION_CHANGE_PASSPHRASE: if (SystemCapabilitiesInfo.ChangeDevicePassphraseSupported == FEATURE_SUPPORTED) { Result = TRUE; } break; case SECURITY_OPERATION_DISABLE_PASSPHRASE: if (SystemCapabilitiesInfo.DisableDeviceSecuritySupported == FEATURE_SUPPORTED) { Result = TRUE; } break; case SECURITY_OPERATION_UNLOCK_DEVICE: if (SystemCapabilitiesInfo.UnlockDeviceSecuritySupported == FEATURE_SUPPORTED) { Result = TRUE; } break; case SECURITY_OPERATION_FREEZE_DEVICE: if (SystemCapabilitiesInfo.FreezeDeviceSecuritySupported == FEATURE_SUPPORTED) { Result = TRUE; } break; case SECURITY_OPERATION_ERASE_DEVICE: if (SystemCapabilitiesInfo.EraseDeviceDataSupported == FEATURE_SUPPORTED) { Result = TRUE; } break; case SECURITY_OPERATION_CHANGE_MASTER_PASSPHRASE: if (SystemCapabilitiesInfo.ChangeMasterPassphraseSupported == FEATURE_SUPPORTED) { Result = TRUE; } break; case SECURITY_OPERATION_MASTER_ERASE_DEVICE: if (SystemCapabilitiesInfo.MasterEraseDeviceDataSupported == FEATURE_SUPPORTED) { Result = TRUE; } break; } Finish: FREE_HII_POINTER(SystemCapabilitiesInfo.PtrInterleaveFormatsSupported); FREE_HII_POINTER(SystemCapabilitiesInfo.PtrInterleaveSize); NVDIMM_EXIT(); return Result; } /** Helper function for VerifyTargetDimms(). Contains logic for checking if a DCPMM is allowed based on the RequireDcpmmsBitfield provided to VerifyTargetDimms. @param[in] pCurrentDimm Pointer to DCPMM to test @param[in] RequireDcpmmsBitfield Indicate what requirements should be validated on the list of DCPMMs discovered. **/ BOOLEAN IsDimmAllowed( IN DIMM *pDimm, IN REQUIRE_DCPMMS RequireDcpmmsBitfield ) { BOOLEAN Allowed = TRUE; // Verify the mutually exclusive REQUIRE_DCPMMS_MANAGEABLE and REQUIRE_DCPMMS_UNMANAGEABLE flags are not both set ASSERT(!((REQUIRE_DCPMMS_MANAGEABLE & RequireDcpmmsBitfield) && (REQUIRE_DCPMMS_UNMANAGEABLE & RequireDcpmmsBitfield))); // Generally we only work with manageable NVDIMMs, which are // an Intel DCPMM with a reasonable FIS API version. if ((REQUIRE_DCPMMS_MANAGEABLE & RequireDcpmmsBitfield) && !IsDimmManageable(pDimm)) { Allowed = FALSE; } if ((REQUIRE_DCPMMS_UNMANAGEABLE & RequireDcpmmsBitfield) && IsDimmManageable(pDimm)) { Allowed = FALSE; } // Verify the mutually exclusive REQUIRE_DCPMMS_FUNCTIONAL and REQUIRE_DCPMMS_NON_FUNCTIONAL flags are not both set ASSERT(!((REQUIRE_DCPMMS_FUNCTIONAL & RequireDcpmmsBitfield) && (REQUIRE_DCPMMS_NON_FUNCTIONAL & RequireDcpmmsBitfield))); // Non-functional DCPMMs generally means just that DDRT is untrained, but there // can be other causes. Keeping previous behavior for now until we split up the // meaning of non-functional more. if ((REQUIRE_DCPMMS_FUNCTIONAL & RequireDcpmmsBitfield) && pDimm->NonFunctional) { Allowed = FALSE; } if ((REQUIRE_DCPMMS_NON_FUNCTIONAL & RequireDcpmmsBitfield) && !pDimm->NonFunctional) { Allowed = FALSE; } // Verify the mutually exclusive REQUIRE_DCPMMS_POPULATION_VIOLATION and REQUIRE_DCPMMS_NO_POPULATION_VIOLATION flags are not both set ASSERT(!((REQUIRE_DCPMMS_POPULATION_VIOLATION & RequireDcpmmsBitfield) && (REQUIRE_DCPMMS_NO_POPULATION_VIOLATION & RequireDcpmmsBitfield))); // Population violation DCPMMs means DCPMMs that are in locations that are not part of a POR configuration if ((REQUIRE_DCPMMS_POPULATION_VIOLATION & RequireDcpmmsBitfield) && !IsDimmInPopulationViolation(pDimm)) { Allowed = FALSE; } if ((REQUIRE_DCPMMS_NO_POPULATION_VIOLATION & RequireDcpmmsBitfield) && IsDimmInPopulationViolation(pDimm)) { Allowed = FALSE; } if ((REQUIRE_DCPMMS_NO_UNMAPPED_POPULATION_VIOLATION & RequireDcpmmsBitfield) && IsDimmInUnmappedPopulationViolation(pDimm)) { Allowed = FALSE; } // Verify the mutually exclusive REQUIRE_DCPMMS_MEDIA_ACCESSIBLE and REQUIRE_DCPMMS_MEDIA_NOT_ACCESSIBLE flags are not both set ASSERT(!((REQUIRE_DCPMMS_MEDIA_ACCESSIBLE & RequireDcpmmsBitfield) && (REQUIRE_DCPMMS_MEDIA_NOT_ACCESSIBLE & RequireDcpmmsBitfield))); if ((REQUIRE_DCPMMS_MEDIA_ACCESSIBLE & RequireDcpmmsBitfield) && DIMM_MEDIA_NOT_ACCESSIBLE(pDimm->BootStatusBitmask)) { Allowed = FALSE; } if ((REQUIRE_DCPMMS_MEDIA_NOT_ACCESSIBLE & RequireDcpmmsBitfield) && !DIMM_MEDIA_NOT_ACCESSIBLE(pDimm->BootStatusBitmask)) { Allowed = FALSE; } return Allowed; } /** Verify target DIMM IDs list. Fill output list of pointers to dimms. If sockets were specified then get all DIMMs from these sockets. If DIMM Ids were provided then check if those DIMMs exist. If there are duplicate DIMM/socket Ids then report error. If specified DIMMs count is 0 then take all Manageable DIMMs. Update CommandStatus structure with any warnings/errors found. @param[in] DimmIds An array of DIMM Ids @param[in] DimmIdsCount Number of items in array of DIMM Ids @param[in] SocketIds An array of Socket Ids @param[in] SocketIdsCount Number of items in array of Socket Ids @param[in] RequireDcpmmsBitfield Indicate what requirements should be validated on the list of DCPMMs discovered. @param[out] pDimms Output array of pointers to verified dimms @param[out] pDimmsNum Number of items in array of pointers to dimms @param[out] pCommandStatus Pointer to command status structure @retval EFI_INVALID_PARAMETER Problem with getting specified DIMMs @retval EFI_SUCCESS All Ok **/ EFI_STATUS EFIAPI VerifyTargetDimms ( IN UINT16 DimmIds[] OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 SocketIds[] OPTIONAL, IN UINT32 SocketIdsCount, IN REQUIRE_DCPMMS RequireDcpmmsBitfield, OUT DIMM *pDimms[MAX_DIMMS], OUT UINT32 *pDimmsNum, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LIST_ENTRY *pCurrentDimmNode = NULL; LIST_ENTRY *pDimmList = NULL; DIMM *pCurrentDimm = NULL; UINT32 Index = 0; UINT32 Index2 = 0; BOOLEAN FoundMatchDimmId = FALSE; BOOLEAN FoundMatchSocketId = FALSE; NVDIMM_ENTRY(); if (pDimms == NULL || pDimmsNum == NULL || pCommandStatus == NULL || DimmIdsCount > MAX_DIMMS || SocketIdsCount > MAX_SOCKETS) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pDimmsNum = 0; pDimmList = &gNvmDimmData->PMEMDev.Dimms; // Input sanity checking if ((SocketIdsCount > 0 && SocketIds == NULL) || (DimmIdsCount > 0 && DimmIds == NULL)) { NVDIMM_ERR("Invalid input parameters"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Verify system dimm list LIST_FOR_EACH(pCurrentDimmNode, pDimmList) { pCurrentDimm = DIMM_FROM_NODE(pCurrentDimmNode); if (pCurrentDimm == NULL) { ReturnCode = EFI_LOAD_ERROR; goto Finish; } } // Process through all dimm and socket ids to give maximal feedback, then // go to Finish // Verify provided dimms ids for (Index = 0; Index < DimmIdsCount; Index++) { pCurrentDimm = GetDimmByPid(DimmIds[Index], &gNvmDimmData->PMEMDev.Dimms); if (pCurrentDimm == NULL) { SetObjStatusForDimm(pCommandStatus, pCurrentDimm, NVM_ERR_DIMM_NOT_FOUND); ReturnCode = EFI_INVALID_PARAMETER; continue; } if (!IsDimmAllowed(pCurrentDimm, RequireDcpmmsBitfield)) { SetObjStatusForDimm(pCommandStatus, pCurrentDimm, NVM_ERR_DIMM_EXCLUDED); ReturnCode = EFI_INVALID_PARAMETER; continue; } for (Index2 = Index+1; Index2 < DimmIdsCount; Index2++) { if (DimmIds[Index] == DimmIds[Index2]) { NVDIMM_ERR("Duplicate dimm id"); SetObjStatusForDimm(pCommandStatus, pCurrentDimm, NVM_ERR_DIMM_ID_DUPLICATED); ReturnCode = EFI_INVALID_PARAMETER; break; } } } // Verify provided socket ids for (Index = 0; Index < SocketIdsCount; Index++) { if (!IsSocketIdValid(SocketIds[Index])) { ResetCmdStatus(pCommandStatus, NVM_ERR_SOCKET_ID_NOT_VALID); ReturnCode = EFI_INVALID_PARAMETER; continue; } // check for duplicate entries for (Index2 = Index+1; Index2 < SocketIdsCount; Index2++) { if (SocketIds[Index] == SocketIds[Index2]) { ResetCmdStatus(pCommandStatus, NVM_ERR_SOCKET_ID_DUPLICATED); ReturnCode = EFI_INVALID_PARAMETER; break; } } } // Go to Finish with any error from previous sections CHECK_RETURN_CODE(ReturnCode, Finish); // Main loop, go through each DCPMM in platform LIST_FOR_EACH(pCurrentDimmNode, pDimmList) { pCurrentDimm = DIMM_FROM_NODE(pCurrentDimmNode); // If it is not an allowed DCPMM, skip it // Error was already thrown if it is a specified DCPMM if (!IsDimmAllowed(pCurrentDimm, RequireDcpmmsBitfield)) { continue; } FoundMatchDimmId = FALSE; FoundMatchSocketId = FALSE; for (Index = 0; Index < DimmIdsCount; Index++) { if (pCurrentDimm->DimmID == DimmIds[Index]) { FoundMatchDimmId = TRUE; } } for (Index = 0; Index < SocketIdsCount; Index++) { if (pCurrentDimm->SocketId == SocketIds[Index]) { FoundMatchSocketId = TRUE; } } // Check if specified socket and dimm ids conflict if (DimmIdsCount > 0 && SocketIdsCount > 0 && (FoundMatchDimmId != FoundMatchSocketId)) { ResetCmdStatus(pCommandStatus, NVM_ERR_SOCKET_ID_INCOMPATIBLE_W_DIMM_ID); goto Finish; } // If unspecified or we found a match if ((DimmIdsCount == 0 && SocketIdsCount == 0) || FoundMatchSocketId == TRUE || FoundMatchDimmId == TRUE) { // Check for buffer overflow if (*pDimmsNum >= MAX_DIMMS) { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); ReturnCode = EFI_BUFFER_TOO_SMALL; goto Finish; } // Add to output dimms list pDimms[(*pDimmsNum)] = pCurrentDimm; (*pDimmsNum)++; } } // sanity checks if (*pDimmsNum == 0) { ResetCmdStatus(pCommandStatus, NVM_ERR_NO_USABLE_DIMMS); ReturnCode = EFI_NOT_FOUND; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Verify target DIMM IDs in list are available for SPI Flash. If DIMM Ids were provided then check if those DIMMs exist in a SPI flashable state and return list of verified dimms. If specified DIMMs count is 0 then return all DIMMS that are in SPI Flashable state. Update CommandStatus structure at the end. @param[in] DimmIds An array of DIMM Ids @param[in] DimmIdsCount Number of items in array of DIMM Ids @param[out] pDimms Output array of pointers to verified dimms @param[out] pDimmsNum Number of items in array of pointers to dimms @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS Success @retval EFI_NOT_FOUND a dimm in DimmIds is not in a flashable state or no dimms found @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI VerifyNonfunctionalTargetDimms( IN UINT16 DimmIds[] OPTIONAL, IN UINT32 DimmIdsCount, OUT DIMM *pDimms[MAX_DIMMS], OUT UINT32 *pDimmsNum, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LIST_ENTRY *pDimmList = NULL; DIMM *pCurrentDimm = NULL; LIST_ENTRY *pCurrentDimmNode = NULL; UINT32 Index = 0; BOOLEAN AllRequestedDimmsVerified = TRUE; *pDimmsNum = 0; pDimmList = &gNvmDimmData->PMEMDev.Dimms; // Input sanity checking if (DimmIdsCount > 0 && DimmIds == NULL) { NVDIMM_ERR("Invalid input parameters"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (DimmIdsCount > 0) { // check if specified DIMMs exist in desired state for (Index = 0; Index < DimmIdsCount; Index++) { pCurrentDimm = GetDimmByPid(DimmIds[Index], pDimmList); if (pCurrentDimm == NULL) { NVDIMM_DBG("Failed on GetDimmByPid. Does DIMM 0x%04x exist?", DimmIds[Index]); AllRequestedDimmsVerified = FALSE; ReturnCode = EFI_NOT_FOUND; goto Finish; } if (!pCurrentDimm->NonFunctional || (pCurrentDimm->SubsystemVendorId != SPD_INTEL_VENDOR_ID) || (pCurrentDimm->SubsystemDeviceId != SPD_DEVICE_ID_15)) { AllRequestedDimmsVerified = FALSE; } else { pCurrentDimm->SmbusAddress.Cpu = (UINT8)(pCurrentDimm->DeviceHandle.NfitDeviceHandle.SocketId); pCurrentDimm->SmbusAddress.Imc = (UINT8)(pCurrentDimm->DeviceHandle.NfitDeviceHandle.MemControllerId); pCurrentDimm->SmbusAddress.Slot = (UINT8)(pCurrentDimm->DeviceHandle.NfitDeviceHandle.MemChannel * MAX_DIMMS_PER_CHANNEL + pCurrentDimm->DeviceHandle.NfitDeviceHandle.DimmNumber); // add dimm to list of verified dimms pCurrentDimm->Signature = DIMM_SIGNATURE; pDimms[(*pDimmsNum)] = pCurrentDimm; (*pDimmsNum)++; } } if (AllRequestedDimmsVerified) { ReturnCode = EFI_SUCCESS; } else { ReturnCode = EFI_NOT_FOUND; } } else { // get all dimms in system in desired state LIST_FOR_EACH(pCurrentDimmNode, pDimmList) { pCurrentDimm = DIMM_FROM_NODE(pCurrentDimmNode); if (pCurrentDimm == NULL) { ReturnCode = EFI_LOAD_ERROR; goto Finish; } if ((pCurrentDimm->NonFunctional) && (pCurrentDimm->SubsystemVendorId == SPD_INTEL_VENDOR_ID) && (pCurrentDimm->SubsystemDeviceId == SPD_DEVICE_ID_15)) { pCurrentDimm->SmbusAddress.Cpu = (UINT8)(pCurrentDimm->DeviceHandle.NfitDeviceHandle.SocketId); pCurrentDimm->SmbusAddress.Imc = (UINT8)(pCurrentDimm->DeviceHandle.NfitDeviceHandle.MemControllerId); pCurrentDimm->SmbusAddress.Slot = (UINT8)(pCurrentDimm->DeviceHandle.NfitDeviceHandle.MemChannel * MAX_DIMMS_PER_CHANNEL + pCurrentDimm->DeviceHandle.NfitDeviceHandle.DimmNumber); pCurrentDimm->Signature = DIMM_SIGNATURE; // add dimm to list of verified dimms pDimms[(*pDimmsNum)] = pCurrentDimm; (*pDimmsNum)++; } } ReturnCode = EFI_SUCCESS; } // sanity checks if (*pDimmsNum == 0) { ResetCmdStatus(pCommandStatus, NVM_ERR_MANAGEABLE_DIMM_NOT_FOUND); ReturnCode = EFI_NOT_FOUND; goto Finish; } else if (*pDimmsNum > MAX_DIMMS) { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Set object status for PMem modules not paired with DDR in case of 2LM @param[in] pDimms Array of pointers to targeted PMem modules only @param[in] pDimmsNum Number of pointers in pDimms @param[out] pCommandStatus Pointer to command status structure @retval EFI_LOAD_ERROR Error in retrieving information from ACPI tables @retval EFI_INVALID_PARAMETER pCommandStatus is NULL @retval EFI_SUCCESS All Ok **/ EFI_STATUS SetObjStatusForPMemNotPairedWithDdr( IN DIMM *pDimms[MAX_DIMMS], IN UINT32 DimmsNum, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_LOAD_ERROR; DIMM *pDimm = NULL; UINT32 Index1 = 0, Index2 = 0; BOOLEAN IsDcpmmPairedWithDdr = FALSE; BOOLEAN NonPorCrossTileSupportedConfig = FALSE; ParsedPmttHeader *pPmttHead = NULL; NVDIMM_ENTRY(); if (NULL == pDimms || NULL == pCommandStatus || DimmsNum == 0 || DimmsNum > MAX_DIMMS) { NVDIMM_DBG("Invalid parameter passed."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = CheckIsNonPorCrossTileSupportedConfig(&NonPorCrossTileSupportedConfig); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CheckIsNonPorCrossTileSupportedConfig failed."); goto Finish; } /** When in a non-POR cross-tile supported configuration, there are no PMem modules that are unusable for 2LM. **/ if (NonPorCrossTileSupportedConfig) { ReturnCode = EFI_SUCCESS; goto Finish; } pPmttHead = gNvmDimmData->PMEMDev.pPmttHead; if (NULL == pPmttHead) { NVDIMM_DBG("Pmtt head not found."); /** This warning message is disabled on Purley platforms (PMTT Rev: 0x1) pPmttHead is NULL for older PMTT revisions **/ ReturnCode = EFI_SUCCESS; goto Finish; } for (Index1 = 0; Index1 < pPmttHead->DCPMModulesNum; Index1++) { pDimm = GetDimmByPid(pPmttHead->ppDCPMModules[Index1]->SmbiosHandle, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL) { NVDIMM_DBG("Failed to retrieve the DCPMM pid %x", pPmttHead->ppDCPMModules[Index1]->SmbiosHandle); goto Finish; } if (!IsPointerInArray((VOID **)pDimms, DimmsNum, pDimm)) { continue; } // Unmanageable, non-functional and population violation PMem modules excluded for Memory Mode if (!IsDimmManageable(pDimm) || pDimm->NonFunctional || IsDimmInPopulationViolation(pDimm)) { continue; } // Check to see if this PMem module is paired with a DDR on the iMc for (Index2 = 0; Index2 < pPmttHead->DDRModulesNum; Index2++) { if ((pPmttHead->ppDDRModules[Index2]->SocketId == pPmttHead->ppDCPMModules[Index1]->SocketId) && (pPmttHead->ppDDRModules[Index2]->DieId == pPmttHead->ppDCPMModules[Index1]->DieId) && (pPmttHead->ppDDRModules[Index2]->MemControllerId == pPmttHead->ppDCPMModules[Index1]->MemControllerId)) { IsDcpmmPairedWithDdr = TRUE; break; } } if (!IsDcpmmPairedWithDdr) { SetObjStatusForDimm(pCommandStatus, pDimm, NVM_WARN_PMEM_MODULE_NOT_PAIRED_FOR_2LM); } IsDcpmmPairedWithDdr = FALSE; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve the list of functional DCPMMs found in NFIT @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmCount The size of pDimms. @param[in] dimmInfoCategories DIMM_INFO_CATEGORIES specifies which (if any) additional FW api calls is desired. If DIMM_INFO_CATEGORY_NONE, then only the properties from the pDimm struct will be populated. @param[out] pDimms The dimm list found in NFIT. @retval EFI_SUCCESS The dimm list was returned properly @retval EFI_INVALID_PARAMETER one or more parameters are NULL or invalid. @retval EFI_NOT_FOUND Dimm not found **/ EFI_STATUS EFIAPI GetDimms( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT32 DimmCount, IN DIMM_INFO_CATEGORIES dimmInfoCategories, OUT DIMM_INFO *pDimms ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Index = 0; LIST_ENTRY *pNode = NULL; DIMM *pCurDimm = NULL; NVDIMM_ENTRY(); /* check input parameters */ if (pThis == NULL || pDimms == NULL) { NVDIMM_DBG("pDimms is NULL"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } LIST_COUNT(pNode, &gNvmDimmData->PMEMDev.Dimms, Index); if (DimmCount > Index) { NVDIMM_DBG("DimmCount is more than DIMM list count"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } SetMem(pDimms, sizeof(*pDimms) * DimmCount, 0); // this clears error mask as well Index = 0; LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Dimms) { pCurDimm = DIMM_FROM_NODE(pNode); if (pCurDimm->NonFunctional == TRUE) { continue; } if (DimmCount <= Index) { NVDIMM_DBG("Array is too small to hold entire DIMM list"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } GetDimmInfo(pCurDimm, dimmInfoCategories, &pDimms[Index]); Index++; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve the list of non-functional PMem modules found in NFIT Note: To properly fill in these fields, it is necessary to call GetDimm() after this with your desired DIMM_INFO_CATEGORIES. @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmCount The size of pDimms. @param[out] pDimms The PMem module list @retval EFI_SUCCESS The module list was returned properly @retval EFI_INVALID_PARAMETER one or more parameter are NULL or invalid. @retval EFI_NOT_FOUND PMem module not found **/ EFI_STATUS EFIAPI GetUninitializedDimms( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT32 DimmCount, OUT DIMM_INFO *pDimms ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Index = 0; LIST_ENTRY *pNode = NULL; DIMM *pCurDimm = NULL; NVDIMM_ENTRY(); if (pThis == NULL || pDimms == NULL) { NVDIMM_DBG("Parameter is NULL"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (DimmCount > MAX_DIMMS) { NVDIMM_DBG("DimmCount is larger than MAX_DIMMS"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } SetMem(pDimms, sizeof(*pDimms) * DimmCount, 0); // this clears error mask as well Index = 0; LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Dimms) { pCurDimm = DIMM_FROM_NODE(pNode); if (pCurDimm->NonFunctional == FALSE) { continue; } if (DimmCount <= Index) { NVDIMM_DBG("Array is too small to hold entire DIMM list"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } InitializeNfitDimmInfoFieldsFromDimm(pCurDimm, &(pDimms[Index])); FillSmbiosInfo(&(pDimms[Index])); pDimms[Index].MemoryType = MEMORYTYPE_DCPM; AsciiStrToUnicodeStrS(pCurDimm->PartNumber, pDimms[Index].PartNumber, PART_NUMBER_STR_LEN); pDimms[Index].FwVer = pCurDimm->FwVer; pDimms[Index].HealthState = HEALTH_NON_FUNCTIONAL; Index++; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve the details about the DIMM specified with pid found in NFIT @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Pid The ID of the dimm to retrieve @param[in] dimmInfoCategories DIMM_INFO_CATEGORIES specifies which (if any) additional FW api calls is desired. If DIMM_INFO_CATEGORY_NONE, then only the properties from the pDimm struct will be populated. @param[out] pDimmInfo A pointer to the dimm found in NFIT @retval EFI_SUCCESS The dimm information was returned properly @retval EFI_INVALID_PARAMETER pDimm is NULL or the dimm with the pid provided does not exist. **/ EFI_STATUS EFIAPI GetDimm( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 Pid, IN DIMM_INFO_CATEGORIES dimmInfoCategories, OUT DIMM_INFO *pDimmInfo ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; NVDIMM_ENTRY(); /* check input parameters */ if (pThis == NULL || pDimmInfo == NULL) { NVDIMM_DBG("One or more parameters are NULL"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } SetMem(pDimmInfo, sizeof(*pDimmInfo), 0); // this clears error mask as well pDimm = GetDimmByPid(Pid, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL) { NVDIMM_DBG("Failed to retrieve the DCPMM pid %x", Pid); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = GetDimmInfo(pDimm, dimmInfoCategories, pDimmInfo); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to convert Dimm to Discovery"); goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #ifdef OS_BUILD /** Retrieve the PMON register values from the dimm @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Pid The ID of the dimm to retrieve @param[in] SmartDataMask This will specify whether or not to return the extra smart data along with the PMON Counter data @param[out] pPayloadPMONRegisters A pointer to the output payload PMON registers @retval EFI_SUCCESS The dimm information was returned properly @retval EFI_INVALID_PARAMETER pDimm is NULL or the dimm with the pid provided does not exist. **/ EFI_STATUS EFIAPI GetPMONRegisters( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 Pid, IN UINT8 SmartDataMask, OUT PMON_REGISTERS *pPayloadPMONRegisters ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; NVDIMM_ENTRY(); /* check input parameters */ if (pThis == NULL) { NVDIMM_DBG("One or more parameters are NULL"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } SetMem(pPayloadPMONRegisters, sizeof(*pPayloadPMONRegisters), 0); // this clears error mask as well pDimm = GetDimmByPid(Pid, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL) { NVDIMM_DBG("Failed to retrieve the DCPMM pid %x", Pid); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = FwCmdGetPMONRegisters(pDimm, SmartDataMask, pPayloadPMONRegisters); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to Get PMON Registers"); goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Set the PMON register values from the dimm @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Pid The ID of the dimm to retrieve @param[in] PMONGroupEnable Specifies which PMON Group to enable @param[out] pPayloadPMONRegisters A pointer to the output payload PMON registers @retval EFI_SUCCESS The dimm information was returned properly @retval EFI_INVALID_PARAMETER pDimm is NULL or the dimm with the pid provided does not exist. **/ EFI_STATUS EFIAPI SetPMONRegisters( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 Pid, IN UINT8 PMONGroupEnable ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; NVDIMM_ENTRY(); /* check input parameters */ if (pThis == NULL) { NVDIMM_DBG("One or more parameters are NULL"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pDimm = GetDimmByPid(Pid, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL) { NVDIMM_DBG("Failed to retrieve the DCPMM pid %x", Pid); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = FwCmdSetPMONRegisters(pDimm, PMONGroupEnable); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to Get PMON Registers"); goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif /** Retrieve the list of sockets (physical processors) in the host server @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pSocketCount The size of the list of sockets. @param[out] ppSockets Pointer to the list of sockets. @retval EFI_SUCCESS The socket list was returned properly or, the platform does not support socket sku limits @retval EFI_INVALID_PARAMETER One or more parameters are NULL. @retval EFI_NOT_FOUND PCAT tables could not be retrieved successfully @retval EFI_DEVICE_ERROR Internal function error @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS EFIAPI GetSockets( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pSocketCount, OUT SOCKET_INFO **ppSockets ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; ParsedPcatHeader *pPcat = NULL; UINT32 Index = 0; UINT32 SocketCount = 0; UINT32 LogicalSocketID = 0; NVDIMM_ENTRY(); if (pThis == NULL || pSocketCount == NULL || ppSockets == NULL) { NVDIMM_DBG("One or more parameters are NULL"); goto Finish; } ReturnCode = GetAcpiPcat(pThis, &pPcat); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to retrieve the PCAT tables."); goto Finish; } if (pPcat != NULL) { SocketCount = pPcat->SocketSkuInfoNum; } else { ReturnCode = EFI_DEVICE_ERROR; goto Finish; } if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pPcat->pPlatformConfigAttr)) { SOCKET_SKU_INFO_TABLE *pSocketSkuInfo = NULL; if (SocketCount == 0 || pPcat->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable == NULL) { NVDIMM_DBG("Platform does not support socket SKU limits."); ReturnCode = EFI_SUCCESS; goto Finish; } *ppSockets = AllocateZeroPool(sizeof(**ppSockets) * SocketCount); if (*ppSockets == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } for (Index = 0; Index < SocketCount; Index++) { if (pPcat->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable[Index] == NULL) { ReturnCode = EFI_DEVICE_ERROR; goto FinishError; } pSocketSkuInfo = pPcat->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable[Index]; (*ppSockets)[Index].SocketId = pSocketSkuInfo->SocketId; (*ppSockets)[Index].MappedMemoryLimit = pSocketSkuInfo->MappedMemorySizeLimit; (*ppSockets)[Index].TotalMappedMemory = pSocketSkuInfo->TotalMemorySizeMappedToSpa; } } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPcat->pPlatformConfigAttr)) { DIE_SKU_INFO_TABLE *pDieSkuInfo = NULL; if (SocketCount == 0 || pPcat->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable == NULL) { NVDIMM_DBG("Platform does not support socket SKU limits."); ReturnCode = EFI_SUCCESS; goto Finish; } *ppSockets = AllocateZeroPool(sizeof(**ppSockets) * SocketCount); if (*ppSockets == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } for (Index = 0; Index < SocketCount; Index++) { if (pPcat->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index] == NULL) { ReturnCode = EFI_DEVICE_ERROR; goto FinishError; } pDieSkuInfo = pPcat->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index]; ReturnCode = GetLogicalSocketIdFromPmtt(pDieSkuInfo->SocketId, pDieSkuInfo->DieId, &LogicalSocketID); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to retrieve logical socket ID"); goto Finish; } (*ppSockets)[Index].SocketId = LogicalSocketID & 0XFFFF; (*ppSockets)[Index].MappedMemoryLimit = pDieSkuInfo->MappedMemorySizeLimit; (*ppSockets)[Index].TotalMappedMemory = pDieSkuInfo->TotalMemorySizeMappedToSpa; } } else { NVDIMM_DBG("Unknown PCAT table revision"); ReturnCode = EFI_NOT_FOUND; goto Finish; } *pSocketCount = SocketCount; ReturnCode = EFI_SUCCESS; goto Finish; FinishError: if (*ppSockets != NULL) { FREE_POOL_SAFE(*ppSockets); } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check NVM device security state Function checks security state of a set of DIMMs. It sets security state to mixed when not all DIMMs have the same state. @param[in] pThis a pointer to EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] pDimmIds Pointer to an array of DIMM IDs @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[out] pSecurityState security state of a DIMM or all DIMMs @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER when pSecurityState is NULL @retval EFI_NOT_FOUND it was not possible to get state of a DIMM @retval EFI_SUCCESS state correctly detected and stored in pSecurityState **/ EFI_STATUS EFIAPI GetSecurityState( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, OUT UINT8 *pSecurityState, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; UINT32 Index = 0; UINT32 SystemSecurityState = 0; UINT32 DimmSecurityState = 0; BOOLEAN IsMixed = FALSE; NVDIMM_ENTRY(); SetMem(pDimms, sizeof(pDimms), 0x0); if (pThis == NULL || pSecurityState == NULL || pDimmIds == NULL || pCommandStatus == NULL) { goto Finish; } ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, NULL, 0, REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_FUNCTIONAL, pDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = GetDimmSecurityState( pDimms[Index], PT_TIMEOUT_INTERVAL, &DimmSecurityState ); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_SECURITY_VIOLATION) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_UNABLE_TO_GET_SECURITY_STATE); } goto Finish; } if (Index == 0) { SystemSecurityState = DimmSecurityState; } if (DimmSecurityState != SystemSecurityState) { IsMixed = TRUE; } } if (IsMixed) { *pSecurityState = SECURITY_MIXED_STATE; } else { ConvertSecurityBitmask(SystemSecurityState, pSecurityState); } ReturnCode = EFI_SUCCESS; SetCmdStatus(pCommandStatus, NVM_SUCCESS); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; }; /** PopulateAppDirectIndex Function tries to populate AppDirectIndex for a Regional goal for all regional goals. @param[out] pNumberedGoals This a pointer to array of regional goal appdirect index structures @param[out] pNumberedGoalsNum Number of items in the pNumberedGoals @param[out] pAppDirectIndex The next appdirect index **/ static void PopulateAppDirectIndex( OUT REGION_GOAL_APPDIRECT_INDEX_TABLE *pNumberedGoals, OUT UINT32 *pNumberedGoalsNum, OUT UINT32 *pAppDirectIndex ) { DIMM *pCurrentDimm = NULL; LIST_ENTRY *pDimmList = NULL; LIST_ENTRY *pCurrentDimmNode = NULL; BOOLEAN AppDirectIndexFound = FALSE; UINT32 Index1 = 0; UINT32 Index2 = 0; pDimmList = &gNvmDimmData->PMEMDev.Dimms; if (pNumberedGoals == NULL || pNumberedGoalsNum == NULL || pAppDirectIndex == NULL){ return; } LIST_FOR_EACH(pCurrentDimmNode, pDimmList) { pCurrentDimm = DIMM_FROM_NODE(pCurrentDimmNode); if (pCurrentDimm == NULL) { return; } if (IsDimmManageable(pCurrentDimm)) { //Iterate over RegionsGoal for current dimm for (Index1 = 0; Index1 < pCurrentDimm->RegionsGoalNum; ++Index1) { AppDirectIndexFound = FALSE; //Iterate over numbered goals to see if it already exists for (Index2 = 0; Index2 < *pNumberedGoalsNum; Index2++) { if (pNumberedGoals[Index2].pRegionGoal == pCurrentDimm->pRegionsGoal[Index1]) { AppDirectIndexFound = TRUE; break; } } if (!AppDirectIndexFound) { pNumberedGoals[*pNumberedGoalsNum].pRegionGoal = pCurrentDimm->pRegionsGoal[Index1]; pNumberedGoals[*pNumberedGoalsNum].AppDirectIndex = *pAppDirectIndex; (*pNumberedGoalsNum)++; (*pAppDirectIndex)++; } } } } } /** Get region goal configuration @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of DIMM IDs @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[out] pConfigGoals pointer to output array @param[out] pConfigGoalsCount number of elements written @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_NO_RESPONSE FW busy for one or more dimms @retval EFI_NOT_FOUND PMem module could not be found @retval EFI_SUCCESS All Ok **/ EFI_STATUS EFIAPI GetGoalConfigs( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds OPTIONAL, IN UINT32 SocketIdsCount, IN CONST UINT32 ConfigGoalTableSize, OUT REGION_GOAL_PER_DIMM_INFO *pConfigGoals, OUT UINT32 *pConfigGoalsCount, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimms[MAX_DIMMS]; DIMM *pCurrentDimm = NULL; REGION_GOAL_PER_DIMM_INFO *pCurrentGoal = NULL; UINT32 DimmsCount = 0; UINT32 ConfigRegionCount = 0; UINT32 Index1 = 0; UINT32 Index2 = 0; UINT32 Index3 = 0; UINT32 NumberedGoalsNum = 0; UINT32 AppDirectIndex = 1; UINT32 SequenceIndex = 0; REGION_GOAL_APPDIRECT_INDEX_TABLE NumberedGoals[MAX_IS_PER_DIMM * MAX_DIMMS]; MEMORY_MODE AllowedMode = MEMORY_MODE_1LM; SetMem(pDimms, sizeof(pDimms), 0x0); SetMem(&NumberedGoals, sizeof(NumberedGoals), 0x0); if (pConfigGoalsCount == NULL || pConfigGoals == NULL || pCommandStatus == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ERR("Some of required pointers are null"); goto Finish; } ReturnCode = RetrieveGoalConfigsFromPlatformConfigData(&gNvmDimmData->PMEMDev.Dimms, FALSE); if (EFI_ERROR(ReturnCode)) { if (EFI_VOLUME_CORRUPTED == ReturnCode) { ResetCmdStatus(pCommandStatus, NVM_ERR_PCD_BAD_DEVICE_CONFIG); } else if (EFI_NO_RESPONSE == ReturnCode) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); } NVDIMM_ERR("ERROR: RetrieveGoalConfigsFromPlatformConfigData"); goto Finish; } //Try to calculate appdirect index for all regional goals for all dimms in advance PopulateAppDirectIndex(NumberedGoals, &NumberedGoalsNum, &AppDirectIndex); ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, pSocketIds, SocketIdsCount, REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_FUNCTIONAL | REQUIRE_DCPMMS_MEDIA_ACCESSIBLE, pDimms, &DimmsCount, pCommandStatus); if (EFI_ERROR(ReturnCode) || pCommandStatus->GeneralStatus != NVM_ERR_OPERATION_NOT_STARTED) { if (ReturnCode == EFI_NOT_FOUND && pCommandStatus->GeneralStatus == NVM_ERR_NO_USABLE_DIMMS) { NVDIMM_DBG("No usable dimms found in GetGoalConfigs"); ResetCmdStatus(pCommandStatus, NVM_SUCCESS); *pConfigGoalsCount = 0; ReturnCode = EFI_SUCCESS; goto Finish; } else { NVDIMM_ERR("ERROR: VerifyTargetDimms"); goto Finish; } } /** Fetch region goals **/ for (Index1 = 0; Index1 < DimmsCount; ++Index1) { pCurrentDimm = pDimms[Index1]; NVDIMM_DBG("Fetch region dimm idx = %d", Index1); NVDIMM_DBG("Fetch region cfg status = 0x%x", pCurrentDimm->GoalConfigStatus); if (!pCurrentDimm->RegionsGoalConfig || pCurrentDimm->GoalConfigStatus == GOAL_CONFIG_STATUS_NO_GOAL_OR_SUCCESS) { NVDIMM_DBG("Fetch region continue..."); continue; // No goal config or success status, so omit it. } if (ConfigRegionCount == ConfigGoalTableSize - 1) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Output table for config goals is to small"); goto Finish; } pCurrentGoal = &pConfigGoals[ConfigRegionCount]; pCurrentGoal->DimmID = pCurrentDimm->DeviceHandle.AsUint32; ReturnCode = GetDimmUid(pCurrentDimm, pCurrentGoal->DimmUid, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("ERROR: GetDimmUid"); goto Finish; } pCurrentGoal->SocketId = pCurrentDimm->SocketId; pCurrentGoal->VolatileSize = pCurrentDimm->VolatileSizeGoal; pCurrentGoal->PersistentRegions = pCurrentDimm->RegionsGoalNum; NVDIMM_DBG("dimm index %d", Index1); NVDIMM_DBG("dimm socket id %x", pCurrentDimm->SocketId); NVDIMM_DBG("dimm volatile size %x", pCurrentDimm->VolatileSizeGoal); NVDIMM_DBG("dimm goal %x", pCurrentDimm->RegionsGoalNum); for (Index2 = 0; Index2 < pCurrentDimm->RegionsGoalNum; ++Index2) { SequenceIndex = pCurrentDimm->pRegionsGoal[Index2]->SequenceIndex; NVDIMM_DBG("region loop %d, region goal size %x, dimms num %x", Index2, pCurrentDimm->pRegionsGoal[Index2]->Size, pCurrentDimm->pRegionsGoal[Index2]->DimmsNum); pCurrentGoal->NumberOfInterleavedDimms[SequenceIndex] = (UINT8)pCurrentDimm->pRegionsGoal[Index2]->DimmsNum; pCurrentGoal->AppDirectSize[SequenceIndex] = pCurrentDimm->pRegionsGoal[Index2]->Size / pCurrentDimm->pRegionsGoal[Index2]->DimmsNum; pCurrentGoal->InterleaveSetType[SequenceIndex] = pCurrentDimm->pRegionsGoal[Index2]->InterleaveSetType; pCurrentGoal->ImcInterleaving[SequenceIndex] = pCurrentDimm->pRegionsGoal[Index2]->ImcInterleaving; pCurrentGoal->ChannelInterleaving[SequenceIndex] = pCurrentDimm->pRegionsGoal[Index2]->ChannelInterleaving; /** Retrieve previously calculated AppDirectIndex **/ for (Index3 = 0; Index3 < NumberedGoalsNum; Index3++) { NVDIMM_DBG("appdir loop %d", Index3); if (NumberedGoals[Index3].pRegionGoal == pCurrentDimm->pRegionsGoal[Index2]) { NVDIMM_DBG("appdir found!"); pCurrentGoal->AppDirectIndex[SequenceIndex] = (UINT8)NumberedGoals[Index3].AppDirectIndex; break; } } } pCurrentGoal->Status = pCurrentDimm->GoalConfigStatus; ConfigRegionCount++; } /** Warning when 2LM mode is off (1LM is selected in the BIOS setup) **/ ReturnCode = AllowedMemoryMode(&AllowedMode); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (!IS_BIOS_VOLATILE_MEMORY_MODE_2LM(AllowedMode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_PLATFORM_NOT_SUPPORT_2LM_MODE); } *pConfigGoalsCount = ConfigRegionCount; Finish: ClearInternalGoalConfigsInfo(&gNvmDimmData->PMEMDev.Dimms); return ReturnCode; } /** Get DIMM alarm thresholds @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmPid The ID of the DIMM @param[in] SensorId Sensor id to retrieve information for @param[out] pNonCriticalThreshold Current non-critical threshold for sensor @param[out] pEnabledState Current enable state for sensor @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER if no DIMM found for DimmPid or input parameter is NULL. @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_DEVICE_ERROR device error detected @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI GetAlarmThresholds ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmPid, IN UINT8 SensorId, OUT INT16 *pNonCriticalThreshold, OUT UINT8 *pEnabledState, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimm = NULL; PT_PAYLOAD_ALARM_THRESHOLDS *pPayloadAlarmThresholds = NULL; INT16 NonCriticalThreshold = THRESHOLD_UNDEFINED; UINT8 EnabledState = ENABLED_STATE_UNDEFINED; NVDIMM_ENTRY(); if (pNonCriticalThreshold == NULL && pEnabledState == NULL) { goto Finish; } if ((SensorId != SENSOR_TYPE_MEDIA_TEMPERATURE) && (SensorId != SENSOR_TYPE_CONTROLLER_TEMPERATURE) && (SensorId != SENSOR_TYPE_PERCENTAGE_REMAINING)) { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_NOT_STARTED); goto Finish; } pDimm = GetDimmByPid(DimmPid, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL) { SetObjStatus(pCommandStatus, DimmPid, NULL, 0, NVM_ERR_DIMM_NOT_FOUND, ObjectTypeDimm); goto Finish; } ReturnCode = FwCmdGetAlarmThresholds(pDimm, &pPayloadAlarmThresholds); if (pPayloadAlarmThresholds == NULL) { ReturnCode = EFI_DEVICE_ERROR; } if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_SECURITY_VIOLATION) { SetObjStatusForDimm(pCommandStatus, pDimm, NVM_ERR_INVALID_SECURITY_STATE); } else { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); } goto Finish; } switch (SensorId) { case SENSOR_TYPE_MEDIA_TEMPERATURE: NonCriticalThreshold = TransformFwTempToRealValue(pPayloadAlarmThresholds->MediaTemperatureThreshold); EnabledState = (UINT8) pPayloadAlarmThresholds->Enable.Separated.MediaTemperature; break; case SENSOR_TYPE_CONTROLLER_TEMPERATURE: NonCriticalThreshold = TransformFwTempToRealValue(pPayloadAlarmThresholds->ControllerTemperatureThreshold); EnabledState = (UINT8) pPayloadAlarmThresholds->Enable.Separated.ControllerTemperature; break; case SENSOR_TYPE_PERCENTAGE_REMAINING: NonCriticalThreshold = (INT16) pPayloadAlarmThresholds->PercentageRemainingThreshold; EnabledState = (UINT8) pPayloadAlarmThresholds->Enable.Separated.PercentageRemaining; break; } if (pNonCriticalThreshold != NULL && NonCriticalThreshold != THRESHOLD_UNDEFINED) { *pNonCriticalThreshold = NonCriticalThreshold; } if (pEnabledState != NULL && EnabledState != ENABLED_STATE_UNDEFINED) { *pEnabledState = EnabledState; } Finish: FREE_POOL_SAFE(pPayloadAlarmThresholds); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Set DIMM alarm thresholds @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] pDimmIds Pointer to an array of DIMM IDs @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[in] SensorId Sensor id to set values for @param[in] NonCriticalThreshold New non-critical threshold for sensor @param[in] EnabledState New enable state for sensor @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All Ok **/ EFI_STATUS EFIAPI SetAlarmThresholds ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, IN UINT8 SensorId, IN INT16 NonCriticalThreshold, IN UINT8 EnabledState, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; PT_PAYLOAD_ALARM_THRESHOLDS *pPayloadAlarmThresholds = NULL; UINT32 Index = 0; PT_DEVICE_CHARACTERISTICS_OUT *pDevCharacteristics = NULL; INT16 ShutdownTemperature = 0; NVDIMM_ENTRY(); SetMem(pDimms, sizeof(pDimms), 0x0); if (pCommandStatus == NULL) { goto Finish; } CHECK_RESULT(BlockMixedSku(pCommandStatus), Finish); if (SensorId != SENSOR_TYPE_MEDIA_TEMPERATURE && SensorId != SENSOR_TYPE_CONTROLLER_TEMPERATURE && SensorId != SENSOR_TYPE_PERCENTAGE_REMAINING) { ResetCmdStatus(pCommandStatus, NVM_ERR_SENSOR_NOT_VALID); goto Finish; } if (EnabledState != ENABLED_STATE_UNDEFINED && EnabledState != ENABLED_STATE_ENABLE && EnabledState != ENABLED_STATE_DISABLE) { ResetCmdStatus(pCommandStatus, NVM_ERR_SENSOR_ENABLED_STATE_INVALID_VALUE); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, NULL, 0, REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_FUNCTIONAL, pDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (NonCriticalThreshold == THRESHOLD_UNDEFINED && EnabledState == ENABLED_STATE_UNDEFINED) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } for ((Index = 0); Index < DimmsNum; (Index++)) { // let's read current values so we'll not overwrite them during setting ReturnCode = FwCmdGetAlarmThresholds(pDimms[Index], &pPayloadAlarmThresholds); if (pPayloadAlarmThresholds == NULL) { ReturnCode = EFI_DEVICE_ERROR; } if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_SECURITY_VIOLATION) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); } else { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); } goto Finish; } if (SensorId == SENSOR_TYPE_CONTROLLER_TEMPERATURE || SensorId == SENSOR_TYPE_MEDIA_TEMPERATURE) { // Get Shutdown threshold for controller ReturnCode = FwCmdDeviceCharacteristics(pDimms[Index], &pDevCharacteristics); if (EFI_ERROR(ReturnCode)) { goto Finish; } } if (SensorId == SENSOR_TYPE_CONTROLLER_TEMPERATURE) { if (NonCriticalThreshold != THRESHOLD_UNDEFINED) { ShutdownTemperature = TransformFwTempToRealValue(pDevCharacteristics->Payload.Fis_2_01.ControllerShutdownThreshold); if (!IS_IN_RANGE(NonCriticalThreshold, TEMPERATURE_THRESHOLD_MIN, ShutdownTemperature)) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_SENSOR_CONTROLLER_TEMP_OUT_OF_RANGE); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pPayloadAlarmThresholds->ControllerTemperatureThreshold = TransformRealValueToFwTemp(NonCriticalThreshold); pPayloadAlarmThresholds->Enable.Separated.ControllerTemperature = TRUE; } if (EnabledState != ENABLED_STATE_UNDEFINED) { pPayloadAlarmThresholds->Enable.Separated.ControllerTemperature = EnabledState; } } if (SensorId == SENSOR_TYPE_MEDIA_TEMPERATURE) { if (NonCriticalThreshold != THRESHOLD_UNDEFINED) { ShutdownTemperature = TransformFwTempToRealValue(pDevCharacteristics->Payload.Fis_2_01.MediaShutdownThreshold); if (!IS_IN_RANGE(NonCriticalThreshold, TEMPERATURE_THRESHOLD_MIN, ShutdownTemperature)) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_SENSOR_MEDIA_TEMP_OUT_OF_RANGE); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pPayloadAlarmThresholds->MediaTemperatureThreshold = TransformRealValueToFwTemp(NonCriticalThreshold); pPayloadAlarmThresholds->Enable.Separated.MediaTemperature = TRUE; } if (EnabledState != ENABLED_STATE_UNDEFINED) { pPayloadAlarmThresholds->Enable.Separated.MediaTemperature = EnabledState; } } if (SensorId == SENSOR_TYPE_PERCENTAGE_REMAINING) { if (NonCriticalThreshold != THRESHOLD_UNDEFINED) { if (!IS_IN_RANGE(NonCriticalThreshold, CAPACITY_THRESHOLD_MIN, CAPACITY_THRESHOLD_MAX)) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_SENSOR_CAPACITY_OUT_OF_RANGE); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pPayloadAlarmThresholds->PercentageRemainingThreshold = (UINT8) NonCriticalThreshold; pPayloadAlarmThresholds->Enable.Separated.PercentageRemaining = TRUE; } if (EnabledState != ENABLED_STATE_UNDEFINED) { pPayloadAlarmThresholds->Enable.Separated.PercentageRemaining = EnabledState; } } ReturnCode = FwCmdSetAlarmThresholds(pDimms[Index], pPayloadAlarmThresholds); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_SECURITY_VIOLATION) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED); } } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_SUCCESS); } } Finish: FREE_POOL_SAFE(pDevCharacteristics); FREE_POOL_SAFE(pPayloadAlarmThresholds); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get NVM DIMM Health Info This FW command is used to retrieve current health of system, including SMART information: * Overall health status * Temperature * Spare blocks * Alarm Trips set (Temperature/Spare Blocks) * Device life span as a percentage * Latched Last shutdown status * Dirty shutdowns * Last shutdown time. * AIT DRAM status * Power Cycles (does not include warm resets or S3 resumes) * Power on time (life of DIMM has been powered on) * Uptime for current power cycle in seconds @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmPid The ID of the DIMM @param[out] pHealthInfo - pointer to structure containing all Health and Smart variables @retval EFI_INVALID_PARAMETER if no DIMM found for DimmPid. @retval EFI_OUT_OF_RESOURCES memory allocation failure @retval EFI_DEVICE_ERROR device error detected @retval EFI_NOT_READY the specified DIMM is unmanageable @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI GetSmartAndHealth ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmPid, OUT SMART_AND_HEALTH_INFO *pHealthInfo ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; PT_PAYLOAD_SMART_AND_HEALTH *pPayloadSmartAndHealth = NULL; PT_DEVICE_CHARACTERISTICS_OUT *pDevCharacteristics = NULL; NVDIMM_ENTRY(); pDimm = GetDimmByPid(DimmPid, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL || pHealthInfo == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (!IsDimmManageable(pDimm)) { ReturnCode = EFI_NOT_READY; goto Finish; } ReturnCode = FwCmdGetSmartAndHealth(pDimm, &pPayloadSmartAndHealth); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = FwCmdDeviceCharacteristics(pDimm, &pDevCharacteristics); if (EFI_ERROR(ReturnCode) || pDevCharacteristics == NULL) { goto Finish; } /** Get common data **/ pHealthInfo->PercentageRemainingValid = (BOOLEAN) pPayloadSmartAndHealth->ValidationFlags.Separated.PercentageRemaining; pHealthInfo->MediaTemperatureValid = (BOOLEAN) pPayloadSmartAndHealth->ValidationFlags.Separated.MediaTemperature; pHealthInfo->ControllerTemperatureValid = (BOOLEAN) pPayloadSmartAndHealth->ValidationFlags.Separated.ControllerTemperature; pHealthInfo->MediaTemperature = TransformFwTempToRealValue(pPayloadSmartAndHealth->MediaTemperature); pHealthInfo->HealthStatus = pPayloadSmartAndHealth->HealthStatus; pHealthInfo->HealthStatusReason = (pPayloadSmartAndHealth->ValidationFlags.Separated.HealthStatusReason) ? pPayloadSmartAndHealth->HealthStatusReason : (UINT16)HEALTH_STATUS_REASON_NONE; pHealthInfo->PercentageRemaining = pPayloadSmartAndHealth->PercentageRemaining; pHealthInfo->LatchedLastShutdownStatus = pPayloadSmartAndHealth->LatchedLastShutdownStatus; /** Get Vendor specific data **/ pHealthInfo->ControllerTemperature = TransformFwTempToRealValue(pPayloadSmartAndHealth->ControllerTemperature); pHealthInfo->UpTime = (UINT32)pPayloadSmartAndHealth->VendorSpecificData.UpTime; pHealthInfo->PowerCycles = pPayloadSmartAndHealth->VendorSpecificData.PowerCycles; pHealthInfo->PowerOnTime = (UINT32)pPayloadSmartAndHealth->VendorSpecificData.PowerOnTime; pHealthInfo->LatchedDirtyShutdownCount = pPayloadSmartAndHealth->LatchedDirtyShutdownCount; pHealthInfo->UnlatchedDirtyShutdownCount = pPayloadSmartAndHealth->VendorSpecificData.UnlatchedDirtyShutdownCount; pHealthInfo->MaxMediaTemperature = TransformFwTempToRealValue(pPayloadSmartAndHealth->VendorSpecificData.MaxMediaTemperature); pHealthInfo->MaxControllerTemperature = TransformFwTempToRealValue(pPayloadSmartAndHealth->VendorSpecificData.MaxControllerTemperature); /** Get Device Characteristics data **/ pHealthInfo->ContrTempShutdownThresh = TransformFwTempToRealValue(pDevCharacteristics->Payload.Fis_2_01.ControllerShutdownThreshold); pHealthInfo->ControllerThrottlingStartThresh = TransformFwTempToRealValue(pDevCharacteristics->Payload.Fis_2_01.ControllerThrottlingStartThreshold); pHealthInfo->ControllerThrottlingStopThresh = TransformFwTempToRealValue(pDevCharacteristics->Payload.Fis_2_01.ControllerThrottlingStopThreshold); pHealthInfo->MediaTempShutdownThresh = TransformFwTempToRealValue(pDevCharacteristics->Payload.Fis_2_01.MediaShutdownThreshold); pHealthInfo->MediaThrottlingStartThresh = TransformFwTempToRealValue(pDevCharacteristics->Payload.Fis_2_01.MediaThrottlingStartThreshold); pHealthInfo->MediaThrottlingStopThresh = TransformFwTempToRealValue(pDevCharacteristics->Payload.Fis_2_01.MediaThrottlingStopThreshold); /** Check triggered alarms **/ pHealthInfo->MediaTemperatureTrip = (pPayloadSmartAndHealth->AlarmTrips.Separated.MediaTemperature != 0); pHealthInfo->ControllerTemperatureTrip = (pPayloadSmartAndHealth->AlarmTrips.Separated.ControllerTemperature != 0); pHealthInfo->PercentageRemainingTrip = (pPayloadSmartAndHealth->AlarmTrips.Separated.PercentageRemaining != 0); /** Copy extended detail bits **/ CopyMem_S(&pHealthInfo->LatchedLastShutdownStatusDetails, sizeof(LAST_SHUTDOWN_STATUS_DETAILS_EXTENDED), pPayloadSmartAndHealth->VendorSpecificData.LatchedLastShutdownExtendedDetails.Raw, sizeof(LAST_SHUTDOWN_STATUS_DETAILS_EXTENDED)); /** Shift extended over, add the original 8 bits **/ pHealthInfo->LatchedLastShutdownStatusDetails = (pHealthInfo->LatchedLastShutdownStatusDetails << sizeof(LAST_SHUTDOWN_STATUS_DETAILS) * 8) + pPayloadSmartAndHealth->VendorSpecificData.LatchedLastShutdownDetails.AllFlags; /** Copy extended detail bits **/ CopyMem_S(&pHealthInfo->UnlatchedLastShutdownStatusDetails, sizeof(LAST_SHUTDOWN_STATUS_DETAILS_EXTENDED), pPayloadSmartAndHealth->VendorSpecificData.UnlatchedLastShutdownExtendedDetails.Raw, sizeof(LAST_SHUTDOWN_STATUS_DETAILS_EXTENDED)); /** Shift extended over, add the original 8 bits **/ pHealthInfo->UnlatchedLastShutdownStatusDetails = (pHealthInfo->UnlatchedLastShutdownStatusDetails << sizeof(LAST_SHUTDOWN_STATUS_DETAILS) * 8) + pPayloadSmartAndHealth->VendorSpecificData.UnlatchedLastShutdownDetails.AllFlags; pHealthInfo->LastShutdownTime = pPayloadSmartAndHealth->VendorSpecificData.LastShutdownTime; pHealthInfo->AitDramEnabled = pPayloadSmartAndHealth->AITDRAMStatus; if ((pPayloadSmartAndHealth->ValidationFlags.Separated.AITDRAMStatus == 0) && (pPayloadSmartAndHealth->HealthStatus < HealthStatusCritical)) { pHealthInfo->AitDramEnabled = AIT_DRAM_ENABLED; } pHealthInfo->ThermalThrottlePerformanceLossPrct = pPayloadSmartAndHealth->VendorSpecificData.ThermalThrottlePerformanceLossPercent; ReturnCode = FwCmdGetErrorCount(pDimm, &pHealthInfo->MediaErrorCount, &pHealthInfo->ThermalErrorCount); if (EFI_ERROR(ReturnCode)) { goto Finish; } Finish: FREE_POOL_SAFE(pDevCharacteristics); FREE_POOL_SAFE(pPayloadSmartAndHealth); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Set security state on multiple PMem modules. If there is a failure on one of the PMem modules, the function does not continue onto the remaining modules but exits with an error. @param[in] pThis a pointer to EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] pDimmIds Pointer to an array of DIMM IDs - if NULL, execute operation on all dimms @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[in] SecurityOperation Security Operation code @param[in] pPassphrase a pointer to string with current passphrase. For default Master Passphrase (0's) use a zero length, null terminated string. @param[in] pNewPassphrase a pointer to string with new passphrase @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER when pLockState is NULL @retval EFI_OUT_OF_RESOURCES couldn't allocate memory for a structure @retval EFI_UNSUPPORTED LockState to be set is not recognized, or mixed sku of DCPMMs detected @retval EFI_DEVICE_ERROR setting state for a DIMM failed @retval EFI_NOT_FOUND a DIMM was not found @retval EFI_NO_RESPONSE FW busy for one or more dimms @retval EFI_SUCCESS security state correctly set **/ EFI_STATUS EFIAPI SetSecurityState( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 SecurityOperation, IN CHAR16 *pPassphrase, IN CHAR16 *pNewPassphrase, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_UNSUPPORTED; EFI_STATUS TempReturnCode = EFI_UNSUPPORTED; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; UINT16 PayloadBufferSize = 0; UINT8 SubOpcode = 0; CHAR8 AsciiPassword[PASSPHRASE_BUFFER_SIZE + 1]; UINT32 Index = 0; UINT32 DimmSecurityState = 0; PT_SET_SECURITY_PAYLOAD *pSecurityPayload = NULL; BOOLEAN NamespaceFound = FALSE; BOOLEAN AreNotPartOfPendingGoal = TRUE; BOOLEAN IsSupported = FALSE; REQUIRE_DCPMMS RequireDcpmmsBitfield = REQUIRE_DCPMMS_MANAGEABLE; DIMM *pCurrentDimm = NULL; LIST_ENTRY *pCurrentDimmNode = NULL; LIST_ENTRY *pDimmList = NULL; UINT8 DimmARSStatus = 0; NVDIMM_ENTRY(); SetMem(pDimms, sizeof(pDimms), 0x0); SetMem(AsciiPassword, sizeof(AsciiPassword), 0x0); IsSupported = IsSecurityOpSupported(SecurityOperation); if (!IsSupported) { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_NOT_SUPPORTED); goto Finish; } if (pCommandStatus == NULL) { goto Finish; } CHECK_RESULT(BlockMixedSku(pCommandStatus), Finish); if (SecurityOperation != SECURITY_OPERATION_SET_PASSPHRASE && SecurityOperation != SECURITY_OPERATION_CHANGE_PASSPHRASE && SecurityOperation != SECURITY_OPERATION_DISABLE_PASSPHRASE && SecurityOperation != SECURITY_OPERATION_UNLOCK_DEVICE && SecurityOperation != SECURITY_OPERATION_ERASE_DEVICE && SecurityOperation != SECURITY_OPERATION_FREEZE_DEVICE && SecurityOperation != SECURITY_OPERATION_CHANGE_MASTER_PASSPHRASE && SecurityOperation != SECURITY_OPERATION_MASTER_ERASE_DEVICE) { ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_SECURITY_OPERATION); } // Erase Device Data operation is supported for Dimms // excluded from POR config. For any other commands, the DCPMM needs // to be in a POR config. if (!(SecurityOperation == SECURITY_OPERATION_MASTER_ERASE_DEVICE || SecurityOperation == SECURITY_OPERATION_ERASE_DEVICE)) { RequireDcpmmsBitfield |= REQUIRE_DCPMMS_NO_UNMAPPED_POPULATION_VIOLATION; } ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, NULL, 0, RequireDcpmmsBitfield, pDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } // Prevent user from enabling security when goal is pending due to BIOS restrictions if (SecurityOperation == SECURITY_OPERATION_SET_PASSPHRASE) { // Check if input DIMMs are not part of a goal ReturnCode = RetrieveGoalConfigsFromPlatformConfigData(&gNvmDimmData->PMEMDev.Dimms, FALSE); if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); goto Finish; } pDimmList = &gNvmDimmData->PMEMDev.Dimms; // Loop through all specified dimms until a match is found for (Index = 0; (Index < DimmsNum) && (AreNotPartOfPendingGoal == TRUE); Index++) { UINT32 NodeCount = 0; LIST_FOR_EACH(pCurrentDimmNode, pDimmList) { pCurrentDimm = DIMM_FROM_NODE(pCurrentDimmNode); if (pCurrentDimm == NULL) { NVDIMM_DBG("Failed on Get Dimm from node %d", NodeCount); ReturnCode = EFI_NOT_FOUND; AreNotPartOfPendingGoal = FALSE; goto Finish; } NodeCount++; // Valid match found and GoalConfigStatus is valid if ((pCurrentDimm->DimmID == pDimmIds[Index]) && (pCurrentDimm->GoalConfigStatus != GOAL_CONFIG_STATUS_NO_GOAL_OR_SUCCESS)) { AreNotPartOfPendingGoal = FALSE; break; } } } } if (!AreNotPartOfPendingGoal) { ResetCmdStatus(pCommandStatus, NVM_ERR_ENABLE_SECURITY_NOT_ALLOWED); ReturnCode = EFI_ACCESS_DENIED; goto Finish; } if ((pPassphrase != NULL && StrLen(pPassphrase) > PASSPHRASE_BUFFER_SIZE) || (pNewPassphrase != NULL && StrLen(pNewPassphrase) > PASSPHRASE_BUFFER_SIZE)) { ResetCmdStatus(pCommandStatus, NVM_ERR_PASSPHRASE_TOO_LONG); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } PayloadBufferSize = IN_PAYLOAD_SIZE; pSecurityPayload = AllocateZeroPool(PayloadBufferSize); if (pSecurityPayload == NULL) { NVDIMM_ERR("Out of memory"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } if (pPassphrase != NULL) { UnicodeStrToAsciiStrS(pPassphrase, AsciiPassword, PASSPHRASE_BUFFER_SIZE + 1); CopyMem_S(&pSecurityPayload->PassphraseCurrent, sizeof(pSecurityPayload->PassphraseCurrent), AsciiPassword, AsciiStrLen(AsciiPassword)); } if (pNewPassphrase != NULL) { UnicodeStrToAsciiStrS(pNewPassphrase, AsciiPassword, PASSPHRASE_BUFFER_SIZE + 1); CopyMem_S(&pSecurityPayload->PassphraseNew, sizeof(pSecurityPayload->PassphraseNew), AsciiPassword, AsciiStrLen(AsciiPassword)); } /** Iterate over DIMMs list, perform all checks and execute the command on each of them. If any check fails stop iteration and skip remaining DIMMs. **/ for (Index = 0; Index < DimmsNum; Index++) { if (!IsDimmSkuSupported(pDimms[Index], SkuStandardSecuritySku)) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_COMMAND_NOT_SUPPORTED_BY_THIS_SKU); ReturnCode = EFI_UNSUPPORTED; goto Finish; } ReturnCode = GetDimmSecurityState(pDimms[Index], PT_TIMEOUT_INTERVAL, &DimmSecurityState); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_SECURITY_VIOLATION) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_UNABLE_TO_GET_SECURITY_STATE); } goto Finish; } if ((SecurityOperation == SECURITY_OPERATION_CHANGE_MASTER_PASSPHRASE) || (SecurityOperation == SECURITY_OPERATION_MASTER_ERASE_DEVICE)) { if (DimmSecurityState & SECURITY_MASK_MASTER_COUNTEXPIRED) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_SECURITY_MASTER_PP_COUNT_EXPIRED); ReturnCode = EFI_ABORTED; goto Finish; } } else { if (DimmSecurityState & SECURITY_MASK_COUNTEXPIRED) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_SECURITY_USER_PP_COUNT_EXPIRED); ReturnCode = EFI_ABORTED; goto Finish; } } /** Verify if operation is possible to perform in current device state and if required passphrases are provided **/ if (SecurityOperation == SECURITY_OPERATION_SET_PASSPHRASE) { /** Set Passphrase (B->H state transition according to FW Interface Spec) SecurityState=NotEnabled,NotLocked,NotFrozen **/ if (!(DimmSecurityState & SECURITY_MASK_ENABLED) && !(DimmSecurityState & SECURITY_MASK_LOCKED) && !(DimmSecurityState & SECURITY_MASK_FROZEN)) { NVDIMM_DBG("Set initial passphrase"); if (pNewPassphrase == NULL) { ResetCmdStatus(pCommandStatus, NVM_ERR_NEW_PASSPHRASE_NOT_PROVIDED); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } SubOpcode = SubopSetPass; } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } } else if (SecurityOperation == SECURITY_OPERATION_CHANGE_PASSPHRASE) { /** Change Passphrase (H->H state transition according to FW Interface Spec) SecurityState=Enabled,NotLocked,NotFrozen -> LockState=Enabled **/ if ((DimmSecurityState & SECURITY_MASK_ENABLED) && !(DimmSecurityState & SECURITY_MASK_LOCKED) && !(DimmSecurityState & SECURITY_MASK_FROZEN)) { NVDIMM_DBG("Change passphrase"); if (pPassphrase == NULL) { ResetCmdStatus(pCommandStatus, NVM_ERR_PASSPHRASE_NOT_PROVIDED); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (pNewPassphrase == NULL) { ResetCmdStatus(pCommandStatus, NVM_ERR_NEW_PASSPHRASE_NOT_PROVIDED); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } SubOpcode = SubopSetPass; } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } } else if (SecurityOperation == SECURITY_OPERATION_DISABLE_PASSPHRASE) { /** Disable Passphrase (H->B state transition according to FW Interface Spec) SecurityState=Enabled,NotLocked,NotFrozen -> LockState=Disabled **/ if ((DimmSecurityState & SECURITY_MASK_ENABLED) && !(DimmSecurityState & SECURITY_MASK_LOCKED) && !(DimmSecurityState & SECURITY_MASK_FROZEN)) { NVDIMM_DBG("Disable passphrase"); if (pPassphrase == NULL) { ResetCmdStatus(pCommandStatus, NVM_ERR_PASSPHRASE_NOT_PROVIDED); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } SubOpcode = SubopDisablePass; } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } } else if (SecurityOperation == SECURITY_OPERATION_UNLOCK_DEVICE) { /** Unlock device (D->H state transition according to FW Interface Spec) SecurityState=Enabled,Locked,NotFrozen -> LockState=Unlocked **/ if ((DimmSecurityState & SECURITY_MASK_ENABLED) && (DimmSecurityState & SECURITY_MASK_LOCKED) && !(DimmSecurityState & SECURITY_MASK_FROZEN)) { if (pPassphrase == NULL) { ResetCmdStatus(pCommandStatus, NVM_ERR_PASSPHRASE_NOT_PROVIDED); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } SubOpcode = SubopUnlockUnit; } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } } else if (SecurityOperation == SECURITY_OPERATION_ERASE_DEVICE) { /** Security Erase (H->E state transition according to FW Interface Spec) SecurityState=Enabled,NotLocked,NotFrozen -> LockState=Disabled **/ if (DimmSecurityState & SECURITY_MASK_MASTER_ENABLED) { if (pPassphrase == NULL) { ResetCmdStatus(pCommandStatus, NVM_ERR_PASSPHRASE_NOT_PROVIDED); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } ReturnCode = FwCmdGetARS(pDimms[Index], &DimmARSStatus); if (LONG_OP_STATUS_IN_PROGRESS == DimmARSStatus) { NVDIMM_ERR("ARS in progress.\n"); ResetCmdStatus(pCommandStatus, NVM_ERR_ARS_IN_PROGRESS); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } ReturnCode = IsNamespaceOnDimms(&pDimms[Index], 1, &NamespaceFound); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (NamespaceFound) { ReturnCode = EFI_ABORTED; SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_SECURE_ERASE_NAMESPACE_EXISTS); goto Finish; } if (!(DimmSecurityState & SECURITY_MASK_FROZEN)) { SubOpcode = SubopSecEraseUnit; pSecurityPayload->PassphraseType = SECURITY_USER_PASSPHRASE; #ifndef OS_BUILD /** Need to call WBINVD before secure erase **/ AsmWbinvd(); #endif } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } } else if (SecurityOperation == SECURITY_OPERATION_FREEZE_DEVICE) { /** Freeze Lock (H->F1 or B->F2 state transition according to FW Interface Spec) SecurityState=Enabled,NotLocked,NotFrozen,Disabled & LockState=Frozen **/ if (!(DimmSecurityState & SECURITY_MASK_FROZEN) && !(DimmSecurityState & SECURITY_MASK_LOCKED)) { SubOpcode = SubopSecFreezeLock; } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } } else if (SecurityOperation == SECURITY_OPERATION_CHANGE_MASTER_PASSPHRASE) { /** SecurityState=Disabled,MasterPassphraseEnabled Master Passphrase count expired via NVM_ERR **/ if (!(DimmSecurityState & SECURITY_MASK_MASTER_ENABLED)) { // starting with FIS 3.2 the enabled bit for master passphrase is not enabled until // the master passphrase is changed from default so skip this check because this might // be initial setting of the passphrase if ( ! (((3 == pDimms[Index]->FwVer.FwApiMajor) && (2 <= pDimms[Index]->FwVer.FwApiMinor)) || (3 < pDimms[Index]->FwVer.FwApiMajor))) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_NOT_SUPPORTED); ReturnCode = EFI_UNSUPPORTED; goto Finish; } } if ((DimmSecurityState & SECURITY_MASK_MASTER_COUNTEXPIRED)) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_SECURITY_MASTER_PP_COUNT_EXPIRED); ReturnCode = EFI_SECURITY_VIOLATION; goto Finish; } if (!(DimmSecurityState & SECURITY_MASK_ENABLED)) { if (pPassphrase == NULL) { ResetCmdStatus(pCommandStatus, NVM_ERR_PASSPHRASE_NOT_PROVIDED); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (pNewPassphrase == NULL) { ResetCmdStatus(pCommandStatus, NVM_ERR_NEW_PASSPHRASE_NOT_PROVIDED); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } SubOpcode = SubopSetMasterPass; } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } } else if (SecurityOperation == SECURITY_OPERATION_MASTER_ERASE_DEVICE) { /** Security Erase (H->E state transition according to FW Interface Spec) SecurityState=MasterPassphraseEnabled,Enabled,NotLocked,NotFrozen -> LockState=Disabled NotSecurityCountExpired **/ if (!(DimmSecurityState & SECURITY_MASK_MASTER_ENABLED)) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_NOT_SUPPORTED); ReturnCode = EFI_UNSUPPORTED; goto Finish; } if ((DimmSecurityState & SECURITY_MASK_MASTER_COUNTEXPIRED)) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_SECURITY_MASTER_PP_COUNT_EXPIRED); ReturnCode = EFI_SECURITY_VIOLATION; goto Finish; } if (!(DimmSecurityState & SECURITY_MASK_FROZEN)) { if (pPassphrase == NULL) { ResetCmdStatus(pCommandStatus, NVM_ERR_PASSPHRASE_NOT_PROVIDED); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } SubOpcode = SubopSecEraseUnit; pSecurityPayload->PassphraseType = SECURITY_MASTER_PASSPHRASE; #ifndef OS_BUILD /** Need to call WBINVD before secure erase **/ AsmWbinvd(); #endif } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } } else { ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_SECURITY_OPERATION); ReturnCode = EFI_UNSUPPORTED; goto Finish; } ReturnCode = SetDimmSecurityState(pDimms[Index], PtSetSecInfo, SubOpcode, PayloadBufferSize, pSecurityPayload, PT_TIMEOUT_INTERVAL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on SetDimmSecurityState, ReturnCode=" FORMAT_EFI_STATUS "", ReturnCode); switch (ReturnCode) { case EFI_ACCESS_DENIED: SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_PASSPHRASE); break; case EFI_NO_RESPONSE: SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_BUSY_DEVICE); break; case EFI_UNSUPPORTED: SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_NOT_SUPPORTED); break; case EFI_NOT_STARTED: SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_MASTER_PASSPHRASE_NOT_SET); break; default: SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED); break; } goto Finish; } #ifndef OS_BUILD /** Need to call WBINVD after unlock or secure erase **/ if (SecurityOperation == SECURITY_OPERATION_ERASE_DEVICE || SecurityOperation == SECURITY_OPERATION_UNLOCK_DEVICE) { AsmWbinvd(); } #endif /** @todo(check on real HW) WARNING: SetDimmSecurityState will not return EFI_ERROR on SECURITY_MASK_COUNTEXPIRED so we have to check it additionally with GetDimmSecurityState. It could be Simics/FW bug. Check how it is done on real HW. **/ ReturnCode = GetDimmSecurityState(pDimms[Index], PT_TIMEOUT_INTERVAL, &DimmSecurityState); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on GetDimmSecurityState, ReturnCode=" FORMAT_EFI_STATUS "", ReturnCode); SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED); ReturnCode = EFI_ABORTED; goto Finish; } else if ((SecurityOperation == SECURITY_OPERATION_CHANGE_MASTER_PASSPHRASE) || (SecurityOperation == SECURITY_OPERATION_MASTER_ERASE_DEVICE)) { if (DimmSecurityState & SECURITY_MASK_MASTER_COUNTEXPIRED) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_SECURITY_MASTER_PP_COUNT_EXPIRED); ReturnCode = EFI_ABORTED; goto Finish; } } else { if (DimmSecurityState & SECURITY_MASK_COUNTEXPIRED) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_SECURITY_USER_PP_COUNT_EXPIRED); ReturnCode = EFI_ABORTED; goto Finish; } } SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_SUCCESS); pDimms[Index]->EncryptionEnabled = ((DimmSecurityState & SECURITY_MASK_ENABLED) != 0); } if (!EFI_ERROR(ReturnCode)) { SetCmdStatus(pCommandStatus, NVM_SUCCESS); } Finish: if (SecurityOperation == SECURITY_OPERATION_UNLOCK_DEVICE || SecurityOperation == SECURITY_OPERATION_ERASE_DEVICE || SecurityOperation == SECURITY_OPERATION_MASTER_ERASE_DEVICE) { TempReturnCode = ReenumerateNamespacesAndISs(TRUE); if (EFI_ERROR(TempReturnCode)) { NVDIMM_DBG("Unable to re-enumerate namespace on unlocked DIMMs. ReturnCode=" FORMAT_EFI_STATUS "", TempReturnCode); } } CleanStringMemory(AsciiPassword); CleanStringMemory((CHAR8 *)(pSecurityPayload->PassphraseCurrent)); CleanStringMemory((CHAR8 *)(pSecurityPayload->PassphraseNew)); FREE_POOL_SAFE(pSecurityPayload); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve an SMBIOS table type 17 or type 20 for a specific DIMM @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Pid The ID of the dimm to retrieve @param[in] Type The Type of SMBIOS table to retrieve @param[out] pTable A pointer to the SMBIOS table @retval EFI_SUCCESS The count was returned properly @retval EFI_INVALID_PARAMETER pTable is NULL @retval EFI_INVALID_PARAMETER DIMM pid is not valid, or Type is not valid @retval EFI_DEVICE_ERROR Failure to retrieve SMBIOS tables from gST @retval EFI_NOT_FOUND Smbios table of requested type was not found for specified device **/ EFI_STATUS EFIAPI GetDimmSmbiosTable( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 Pid, IN UINT8 Type, OUT SMBIOS_STRUCTURE_POINTER *pTable ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; #ifndef OS_BUILD SMBIOS_STRUCTURE_POINTER DmiPhysicalDev; SMBIOS_STRUCTURE_POINTER DmiDeviceMappedAddr; SMBIOS_VERSION SmbiosVersion; DIMM *pDimm = NULL; NVDIMM_ENTRY(); ZeroMem(&DmiPhysicalDev, sizeof(DmiPhysicalDev)); ZeroMem(&DmiDeviceMappedAddr, sizeof(DmiDeviceMappedAddr)); ZeroMem(&SmbiosVersion, sizeof(SmbiosVersion)); /* check table pointer */ if (pTable == NULL) { NVDIMM_DBG("pTable is NULL"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /* Ensure that the Dimm Pid is valid */ pDimm = GetDimmByPid(Pid, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL) { NVDIMM_DBG("Failed to retrieve the DIMM with pid %x", Pid); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /** Dynamically retrieve SMBIOS tables from gST **/ ReturnCode = GetDmiMemdevInfo(Pid, &DmiPhysicalDev, &DmiDeviceMappedAddr, &SmbiosVersion); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failure to retrieve SMBIOS tables"); goto Finish; } /* populate the table */ if (Type == SMBIOS_TYPE_MEM_DEV) { if (DmiPhysicalDev.Raw != NULL) { pTable->Raw = DmiPhysicalDev.Raw; } else { NVDIMM_ERR("SMBIOS type 17 table doesn't exist for Dimm 0x%04x", Pid); ReturnCode = EFI_NOT_FOUND; goto Finish; } } else if (Type == SMBIOS_TYPE_MEM_DEV_MAPPED_ADDR) { if (DmiDeviceMappedAddr.Raw != NULL) { pTable->Raw = DmiDeviceMappedAddr.Raw; } else { NVDIMM_ERR("SMBIOS type 20 table doesn't exist for Dimm 0x%04x", Pid); ReturnCode = EFI_NOT_FOUND; goto Finish; } } else { NVDIMM_DBG("Type %d is not valid", Type); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); #endif return ReturnCode; } /** Retrieve the NFIT ACPI table @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] ppNFit A pointer to the output NFIT table @retval EFI_SUCCESS Ok @retval EFI_OUT_OF_RESOURCES Problem with allocating memory @retval EFI_NOT_FOUND PCAT tables not found @retval EFI_INVALID_PARAMETER pNFit is NULL **/ EFI_STATUS EFIAPI GetAcpiNFit ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT ParsedFitHeader **ppNFit ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; if (ppNFit == NULL) { goto Finish; } if (gNvmDimmData->PMEMDev.pFitHead != NULL) { *ppNFit = gNvmDimmData->PMEMDev.pFitHead; ReturnCode = EFI_SUCCESS; } else { NVDIMM_ERR("NFIT does not exist"); ReturnCode = EFI_NOT_FOUND; } Finish: return ReturnCode; } /** Retrieve the PCAT ACPI table @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] ppPcat output buffer with PCAT tables @retval EFI_SUCCESS Ok @retval EFI_OUT_OF_RESOURCES Problem with allocating memory @retval EFI_NOT_FOUND PCAT tables not found **/ EFI_STATUS EFIAPI GetAcpiPcat ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT ParsedPcatHeader **ppPcat ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; if (ppPcat == NULL) { goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead != NULL) { *ppPcat = gNvmDimmData->PMEMDev.pPcatHead; ReturnCode = EFI_SUCCESS; } else { NVDIMM_DBG("PCAT does not exist"); ReturnCode = EFI_NOT_FOUND; } Finish: return ReturnCode; } /** Retrieve the PMTT ACPI table @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] ppPMTT output buffer with PMTT tables. This buffer must be freed by caller. @retval EFI_SUCCESS Ok @retval EFI_OUT_OF_RESOURCES Problem with allocating memory @retval EFI_NOT_FOUND PCAT tables not found **/ EFI_STATUS EFIAPI GetAcpiPMTT( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT VOID **ppPMTT ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; #ifdef OS_BUILD UINT32 Size = 0; PbrContext *pContext = PBR_CTX(); #endif if (ppPMTT == NULL) { goto Finish; } #ifdef OS_BUILD if (PBR_PLAYBACK_MODE == PBR_GET_MODE(pContext)) { ReturnCode = PbrGetTableRecord(pContext, PBR_RECORD_TYPE_PMTT, ppPMTT, &Size); } else { ReturnCode = get_pmtt_table((EFI_ACPI_DESCRIPTION_HEADER**)ppPMTT, &Size); } if (EFI_ERROR(ReturnCode) || NULL == *ppPMTT) { NVDIMM_DBG("Failed to retrieve PMTT table."); ReturnCode = EFI_NOT_FOUND; goto Finish; } #else EFI_ACPI_DESCRIPTION_HEADER *pTempPMTT = NULL; UINT32 PmttTableLength = 0; CHECK_RESULT(GetAcpiTables(gST, NULL, NULL, &pTempPMTT), Finish); // Allocate and copy the buffer to be consistent with OS call PmttTableLength = pTempPMTT->Length; *ppPMTT = AllocatePool(PmttTableLength); if (NULL == *ppPMTT) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } CopyMem_S(*ppPMTT, PmttTableLength, pTempPMTT, PmttTableLength); #endif Finish: return ReturnCode; } /** Get Platform Config Data The caller is responsible for freeing ppDimmPcdInfo by using FreeDimmPcdInfoArray. @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] PcdTarget Target PCD partition: ALL=0, CONFIG=1, NAMESPACES=2 @param[in] pDimmIds Pointer to an array of DIMM IDs @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[out] ppDimmPcdInfo Pointer to output array of PCDs @param[out] pDimmPcdInfoCount Number of items in Dimm PCD Info @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more input parameters are NULL @retval EFI_NO_RESPONSE FW busy for one or more dimms @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS EFIAPI GetPcd( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT8 PcdTarget, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, OUT DIMM_PCD_INFO **ppDimmPcdInfo, OUT UINT32 *pDimmPcdInfoCount, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsCount = 0; UINT32 Index = 0; UINT32 IndexNew = 0; NVDIMM_CONFIGURATION_HEADER *pPcdConfHeader = NULL; LABEL_STORAGE_AREA *pLabelStorageArea = NULL; NVDIMM_ENTRY(); ZeroMem(pDimms, sizeof(pDimms)); if (pThis == NULL || ppDimmPcdInfo == NULL || pDimmPcdInfoCount == NULL || pCommandStatus == NULL) { goto Finish; } //Set initial value of *ppDimmPcdInfo *ppDimmPcdInfo = NULL; ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, NULL, 0, REQUIRE_DCPMMS_MANAGEABLE, pDimms, &DimmsCount, pCommandStatus); if (EFI_ERROR(ReturnCode) || pCommandStatus->GeneralStatus != NVM_ERR_OPERATION_NOT_STARTED) { goto Finish; } // Redo the VerifyTargetDimms output to not include media inacessible modules // and set the object status accordingly for these modules. // This will be re-done in VerifyTargetDimms in the near future IndexNew = 0; for (Index = 0; Index < DimmsCount; Index++) { if (DIMM_MEDIA_NOT_ACCESSIBLE(pDimms[Index]->BootStatusBitmask)) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_MEDIA_NOT_ACCESSIBLE); continue; } pDimms[IndexNew] = pDimms[Index]; IndexNew++; } DimmsCount = IndexNew; *ppDimmPcdInfo = AllocateZeroPool(sizeof(**ppDimmPcdInfo) * DimmsCount); if (*ppDimmPcdInfo == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } for (Index = 0; Index < DimmsCount; Index++) { pPcdConfHeader = NULL; pLabelStorageArea = NULL; (*ppDimmPcdInfo)[Index].DimmId = pDimms[Index]->DeviceHandle.AsUint32; ReturnCode = GetDimmUid(pDimms[Index], (*ppDimmPcdInfo)[Index].DimmUid, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (PcdTarget == PCD_TARGET_ALL || PcdTarget == PCD_TARGET_CONFIG) { ReturnCode = GetPlatformConfigDataOemPartition(pDimms[Index], FALSE, &pPcdConfHeader); if (ReturnCode == EFI_NO_MEDIA) { continue; } #ifdef MEMORY_CORRUPTION_WA if (ReturnCode == EFI_DEVICE_ERROR) { ReturnCode = GetPlatformConfigDataOemPartition(pDimms[Index], FALSE, &pPcdConfHeader); } #endif // MEMORY_CORRUPTIO_WA if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_NO_RESPONSE) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); goto Finish; } NVDIMM_DBG("GetPlatformConfigDataOemPartition returned: " FORMAT_EFI_STATUS "", ReturnCode); SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_GET_PCD_FAILED); goto Finish; } (*ppDimmPcdInfo)[Index].pConfHeader = pPcdConfHeader; } if (PcdTarget == PCD_TARGET_ALL || PcdTarget == PCD_TARGET_NAMESPACES) { #ifndef OS_BUILD if (pDimms[Index]->LsaStatus == LSA_NOT_INIT || pDimms[Index]->LsaStatus == LSA_COULD_NOT_READ_NAMESPACES) { continue; } #endif // OS_BUILD ReturnCode = ReadLabelStorageArea(pDimms[Index]->DimmID, &pLabelStorageArea); #ifdef OS_BUILD if (ReturnCode == EFI_NOT_FOUND) { NVDIMM_DBG("LSA not found on DIMM 0x%x", pDimms[Index]->DeviceHandle.AsUint32); pDimms[Index]->LsaStatus = LSA_NOT_INIT; continue; } else if (ReturnCode == EFI_ACCESS_DENIED) { NVDIMM_DBG("LSA not found on DIMM 0x%x", pDimms[Index]->DeviceHandle.AsUint32); pDimms[Index]->LsaStatus = LSA_COULD_NOT_READ_NAMESPACES; continue; } else if (EFI_ERROR(ReturnCode) && ReturnCode != EFI_ACCESS_DENIED) { pDimms[Index]->LsaStatus = LSA_CORRUPTED; /** If the LSA is corrupted, we do nothing - it may be a driver mismach between UEFI and the OS, so we don't want to "kill" a valid configuration **/ NVDIMM_DBG("LSA corrupted on DIMM 0x%x", pDimms[Index]->DeviceHandle.AsUint32); NVDIMM_DBG("Error in retrieving the LSA: " FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } pDimms[Index]->LsaStatus = LSA_OK; #else // OS_BUILD if (EFI_ERROR(ReturnCode) && ReturnCode != EFI_ACCESS_DENIED) { NVDIMM_DBG("Error in retrieving the LSA: " FORMAT_EFI_STATUS "", ReturnCode); goto Finish; } #endif // OS_BUILD (*ppDimmPcdInfo)[Index].pLabelStorageArea = pLabelStorageArea; } } *pDimmPcdInfoCount = DimmsCount; ReturnCode = EFI_SUCCESS; pCommandStatus->GeneralStatus = NVM_SUCCESS; Finish: if (EFI_ERROR(ReturnCode) && ppDimmPcdInfo != NULL && *ppDimmPcdInfo != NULL && pDimmPcdInfoCount != NULL) { FreeDimmPcdInfoArray(*ppDimmPcdInfo, *pDimmPcdInfoCount); *ppDimmPcdInfo = NULL; } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Modifies select partition data from the PCD @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of DIMM IDs @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[in] ConfigIdMask Bitmask that defines which config to delete @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more input parameters are NULL @retval EFI_NO_RESPONSE FW busy for one or more dimms @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS EFIAPI ModifyPcdConfig( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT32 ConfigIdMask, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_STATUS TmpReturnCode = EFI_SUCCESS; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsCount = 0; UINT32 Index = 0; UINT32 SecurityState = 0; NVDIMM_CONFIGURATION_HEADER *pConfigHeader = NULL; // Only need to zero out headers, not the whole 64K partition. // Adding 256 bytes just to make sure UINT32 ConfigSize = sizeof(NVDIMM_CONFIGURATION_HEADER) + sizeof(NVDIMM_PLATFORM_CONFIG_INPUT) + sizeof(NVDIMM_CURRENT_CONFIG) + sizeof(NVDIMM_PLATFORM_CONFIG_OUTPUT) + 256; NVDIMM_ENTRY(); ZeroMem(pDimms, sizeof(pDimms)); if (pThis == NULL || pCommandStatus == NULL) { goto Finish; } //only allow valid config id options if (0x0 == ConfigIdMask || 0x0 != (ConfigIdMask & ~DELETE_PCD_CONFIG_ALL_MASK)) { goto Finish; } ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, NULL, 0, REQUIRE_DCPMMS_MANAGEABLE, pDimms, &DimmsCount, pCommandStatus); if (EFI_ERROR(ReturnCode) || pCommandStatus->GeneralStatus != NVM_ERR_OPERATION_NOT_STARTED) { goto Finish; } //iterate through all dimms for (Index = 0; Index < DimmsCount; Index++) { // Reject dimms that are media inaccessible // TODO: Move into VerifyTargetDimms if (DIMM_MEDIA_NOT_ACCESSIBLE(pDimms[Index]->BootStatusBitmask)) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_MEDIA_NOT_ACCESSIBLE_CANNOT_CONTINUE); ReturnCode = EFI_UNSUPPORTED; continue; } TmpReturnCode = GetDimmSecurityState(pDimms[Index], PT_TIMEOUT_INTERVAL, &SecurityState); if (EFI_ERROR(TmpReturnCode)) { KEEP_ERROR(ReturnCode, TmpReturnCode); SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_UNABLE_TO_GET_SECURITY_STATE); NVDIMM_DBG("Failed to get DIMM security state"); goto Finish; } //don't allow deleting anything from PCD if device is locked if (!IsConfiguringAllowed(SecurityState)) { KEEP_ERROR(ReturnCode, EFI_ACCESS_DENIED); SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); NVDIMM_DBG("Locked DIMM discovered : 0x%x", pDimms[Index]->DeviceHandle.AsUint32); continue; } //zero LSA if (ConfigIdMask & DELETE_PCD_CONFIG_LSA_MASK) { TmpReturnCode = ZeroLabelStorageAreaHeader(pDimms[Index]->DimmID); if (EFI_ERROR(TmpReturnCode)) { KEEP_ERROR(ReturnCode, TmpReturnCode); SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED); NVDIMM_DBG("Error in zero-ing the LSA: " FORMAT_EFI_STATUS "", TmpReturnCode); continue; } else { NVDIMM_DBG("Zero'ed the LSA on DIMM : 0x%x", pDimms[Index]->DeviceHandle.AsUint32); } } //if any of the PCD configuration bits were set if (ConfigIdMask & (DELETE_PCD_CONFIG_CIN_MASK | DELETE_PCD_CONFIG_COUT_MASK | DELETE_PCD_CONFIG_CCUR_MASK)) { FREE_POOL_SAFE(pConfigHeader); //read partition 1 where CCUR/CIN/COUT resides //we are only going to modify the DATA SIZE and START OFFSET values in the header before writing it back out TmpReturnCode = GetPlatformConfigDataOemPartition(pDimms[Index], TRUE, &pConfigHeader); if (EFI_ERROR(TmpReturnCode)) { KEEP_ERROR(ReturnCode, TmpReturnCode); SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_GET_PCD_FAILED); NVDIMM_DBG("Failed to get PCD"); continue; } //clear CIN if (ConfigIdMask & DELETE_PCD_CONFIG_CIN_MASK) { pConfigHeader->ConfInputDataSize = 0x0; pConfigHeader->ConfInputStartOffset = 0x0; } //clear COUT if (ConfigIdMask & DELETE_PCD_CONFIG_COUT_MASK) { pConfigHeader->ConfOutputDataSize = 0x0; pConfigHeader->ConfOutputStartOffset = 0x0; } //clear CCUR if (ConfigIdMask & DELETE_PCD_CONFIG_CCUR_MASK) { pConfigHeader->CurrentConfDataSize = 0x0; pConfigHeader->CurrentConfStartOffset = 0x0; } //values in partition have changed so we need to recalculate the checksum before writing back to PCD GenerateChecksum(pConfigHeader, pConfigHeader->Header.Length, PCAT_TABLE_HEADER_CHECKSUM_OFFSET); //write full partition 1 back to PCD with updated values TmpReturnCode = SetPlatformConfigDataOemPartition(pDimms[Index], pConfigHeader, ConfigSize); if (EFI_ERROR(TmpReturnCode)) { KEEP_ERROR(ReturnCode, TmpReturnCode); SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED); NVDIMM_DBG("Failed to set PCD"); continue; } } SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_SUCCESS); } ReenumerateNamespacesAndISs(TRUE); Finish: FREE_POOL_SAFE(pConfigHeader); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Extracts from the region structure the minimal data that is needed by the CLI. @param[in] pRegion pointer to the Region structure @param[out] pRegionMin pointer to the minimal region data structure @retval EFI_SUCCESS when everything succeeds. @retval EFI_INVALID_ARGUMENT if at least one of the input arguments equals NULL. **/ STATIC EFI_STATUS GetRegionMinimalInfo( IN NVM_IS *pRegion, OUT REGION_INFO *pRegionMin ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; DIMM_REGION *pDimmRegion = NULL; LIST_ENTRY *pDimmRegionNode = NULL; NVDIMM_ENTRY(); if (pRegion == NULL || pRegionMin == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pRegionMin->RegionId = pRegion->RegionId; pRegionMin->SocketId = pRegion->SocketId; DetermineRegionType(pRegion, &pRegionMin->RegionType); pRegionMin->Capacity = pRegion->Size; if (pRegion->InterleaveSetCookie != 0) { pRegionMin->CookieId = pRegion->InterleaveSetCookie; } else { pRegionMin->CookieId = pRegion->InterleaveSetCookie_1_1; } ReturnCode = DetermineRegionHealth(pRegion, &pRegionMin->Health); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = GetFreeRegionCapacity(pRegion, &pRegionMin->FreeCapacity); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = ADNamespaceMinAndMaxAvailableSizeOnIS(pRegion, &pRegionMin->AppDirNamespaceMinSize, &pRegionMin->AppDirNamespaceMaxSize); if (EFI_ERROR(ReturnCode)) { goto Finish; } pRegionMin->DimmIdCount = 0; LIST_FOR_EACH(pDimmRegionNode, &pRegion->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pDimmRegionNode); pDimm = pDimmRegion->pDimm; pRegionMin->DimmId[pRegionMin->DimmIdCount] = (UINT16)pDimm->DeviceHandle.AsUint32; pRegionMin->DimmIdCount++; } BubbleSort(pRegionMin->DimmId, pRegionMin->DimmIdCount, sizeof(pRegionMin->DimmId[0]), SortRegionDimmId); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve the number of regions in the system @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] UseNfit flag to indicate NFIT usage @param[out] pCount The number of regions found. @retval EFI_SUCCESS The count was returned properly @retval EFI_INVALID_PARAMETER pCount is NULL. @retval EFI_NO_RESPONSE FW busy for one or more dimms **/ EFI_STATUS EFIAPI GetRegionCount( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN BOOLEAN UseNfit, OUT UINT32 *pCount ) { EFI_STATUS Rc = EFI_SUCCESS; LIST_ENTRY *pRegionNode = NULL; LIST_ENTRY *pRegionList = NULL; NVDIMM_ENTRY(); /** check input parameters **/ if (pCount == NULL) { NVDIMM_DBG("pCount is NULL"); Rc = EFI_INVALID_PARAMETER; goto Finish; } *pCount = 0; Rc = ReenumerateNamespacesAndISs(FALSE); if (EFI_ERROR(Rc)) { if (EFI_NO_RESPONSE == Rc) { goto Finish; } } Rc = GetRegionList(&pRegionList, UseNfit); if (EFI_NO_RESPONSE == Rc) { goto Finish; } LIST_FOR_EACH(pRegionNode, pRegionList) { (*pCount)++; } Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** Sorts the region list by Id @param[in out] pRegion1 A pointer to the Regions. @param[in out] pRegion2 A pointer to the copy of Regions. @retval int returns 0,-1, 0 **/ INT32 SortRegionInfoById(VOID *pRegion1, VOID *pRegion2) { REGION_INFO *pRegionA = (REGION_INFO *)pRegion1; REGION_INFO *pRegionB = (REGION_INFO *)pRegion2; if (pRegionA->RegionId == pRegionB->RegionId) { return 0; } else if (pRegionA->RegionId < pRegionB->RegionId) { return -1; } else { return 1; } } /** Sorts the DimmIds list by Id @param[in out] pDimmId1 A pointer to the pDimmId list. @param[in out] pDimmId2 A pointer to the copy of pDimmId list. @retval int returns 0,-1, 0 **/ INT32 SortRegionDimmId(VOID *pDimmId1, VOID *pDimmId2) { if (*(UINT16 *)pDimmId1 == *(UINT16 *)pDimmId2) { return 0; } else if (*(UINT16 *)pDimmId1 < *(UINT16 *)pDimmId2) { return -1; } else { return 1; } } /** Retrieve the region list @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Count The number of regions. @param[in] UseNfit flag to indicate NFIT usage @param[out] pRegions The region list @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS The region list was returned properly @retval EFI_INVALID_PARAMETER pRegions is NULL. @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system @retval EFI_NO_RESPONSE FW busy on one or more dimms **/ EFI_STATUS EFIAPI GetRegions( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT32 Count, IN BOOLEAN UseNfit, OUT REGION_INFO *pRegions, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS Rc = EFI_SUCCESS; UINT32 Index = 0; NVM_IS *pCurRegion = NULL; LIST_ENTRY *pCurRegionNode = NULL; LIST_ENTRY *pRegionList = NULL; NVDIMM_ENTRY(); //This would be just MAX_REGIONS, except there is some HII constraint //making that figure artificially low. Will update when resolved. if (Count > MAX_SOCKETS * MAX_REGIONS_PER_SOCKET) { Rc = EFI_INVALID_PARAMETER; ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_PARAMETER); NVDIMM_WARN("Count passed exceeds the platform maximum region count."); goto Finish; } Rc = GetRegionList(&pRegionList, UseNfit); if (EFI_ERROR(Rc)) { if (EFI_NO_RESPONSE == Rc) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); } goto Finish; } /** check input parameters **/ if (pRegions == NULL || pCommandStatus == NULL) { NVDIMM_DBG("Invalid parameter"); Rc = EFI_INVALID_PARAMETER; goto Finish; } #ifdef OS_BUILD Rc = InitializeNamespaces(); if (EFI_ERROR(Rc)) { NVDIMM_WARN("Failed to initialize Namespaces, error = " FORMAT_EFI_STATUS ".", Rc); Rc = EFI_SUCCESS; // we don't want to fail the GetRegions function in case the Namespace Initialization error } #endif ZeroMem(pRegions, sizeof(*pRegions) * Count); LIST_FOR_EACH(pCurRegionNode, pRegionList) { pCurRegion = IS_FROM_NODE(pCurRegionNode); if (Count <= Index) { NVDIMM_DBG("Array is too small to hold entire region list"); Rc = EFI_INVALID_PARAMETER; break; } Rc = GetRegionMinimalInfo(pCurRegion, &pRegions[Index]); if (EFI_ERROR(Rc)) { NVDIMM_WARN("Failed to get region minimal info, error = " FORMAT_EFI_STATUS ".", Rc); goto Finish; } Index++; } BubbleSort(pRegions, Count, sizeof(*pRegions), SortRegionInfoById); Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** Retrieve the details about the region specified with region id @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] RegionId The region id of the region to retrieve @param[out] pRegion A pointer to the region @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS The region was returned properly @retval EFI_INVALID_PARAMETER pRegion is NULL @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system **/ EFI_STATUS EFIAPI GetRegion( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 RegionId, OUT REGION_INFO *pRegionInfo, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS Rc = EFI_SUCCESS; NVM_IS *pRegion = NULL; LIST_ENTRY *pRegionList = NULL; NVDIMM_ENTRY(); Rc = ReenumerateNamespacesAndISs(FALSE); if (EFI_ERROR(Rc)) { if ((Rc == EFI_NOT_FOUND && IsLsaNotInitializedOnADimm())) { NVDIMM_WARN("Failure to refresh Namespaces is because LSA not initialized"); } else { goto Finish; } } Rc = GetRegionList(&pRegionList, FALSE); if (pRegionList == NULL) { goto Finish; } Rc = EFI_SUCCESS; if (pRegionInfo == NULL || pCommandStatus == NULL) { NVDIMM_DBG("Invalid parameter"); Rc = EFI_INVALID_PARAMETER; goto Finish; } ZeroMem(pRegionInfo, sizeof(*pRegionInfo)); pRegion = GetRegionById(pRegionList, RegionId); if (pRegion == NULL) { NVDIMM_DBG("There is no %d region id on the list of regions", RegionId); Rc = EFI_INVALID_PARAMETER; } else { GetRegionMinimalInfo(pRegion, pRegionInfo); } Finish: NVDIMM_EXIT_I64(Rc); return Rc; } /** Gather info about total capacities on all dimms @param[in] pThis a pointer to EFI_DCPMM_CONFIG2_PROTOCOL instance @param[out] pMemoryResourcesInfo structure filled with required information @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_ABORTED PCAT tables not found @retval Other errors failure of FW commands @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI GetMemoryResourcesInfo( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT MEMORY_RESOURCES_INFO *pMemoryResourcesInfo ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS PreservedReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); CHECK_NULL_ARG(pThis, Finish); CHECK_NULL_ARG(pMemoryResourcesInfo, Finish); // Make sure we start with unknown values ZeroMem(pMemoryResourcesInfo, sizeof(*pMemoryResourcesInfo)); pMemoryResourcesInfo->RawCapacity = ACPI_TABLE_VALUE_UNKNOWN; pMemoryResourcesInfo->VolatileCapacity = ACPI_TABLE_VALUE_UNKNOWN; pMemoryResourcesInfo->AppDirectCapacity = ACPI_TABLE_VALUE_UNKNOWN; pMemoryResourcesInfo->UnconfiguredCapacity = ACPI_TABLE_VALUE_UNKNOWN; pMemoryResourcesInfo->InaccessibleCapacity = ACPI_TABLE_VALUE_UNKNOWN; pMemoryResourcesInfo->ReservedCapacity = ACPI_TABLE_VALUE_UNKNOWN; pMemoryResourcesInfo->DDRRawCapacity = ACPI_TABLE_VALUE_UNKNOWN; pMemoryResourcesInfo->DDRCacheCapacity = ACPI_TABLE_VALUE_UNKNOWN; pMemoryResourcesInfo->DDRVolatileCapacity = ACPI_TABLE_VALUE_UNKNOWN; pMemoryResourcesInfo->DDRInaccessibleCapacity = ACPI_TABLE_VALUE_UNKNOWN; pMemoryResourcesInfo->PcdInvalid = FALSE; // Don't fail out *immediately* if one of them fails. Fail out at the end // Get DCPMM Sizes CHECK_RESULT_CONTINUE_PRESERVE_ERROR(GetTotalDcpmmCapacities(&gNvmDimmData->PMEMDev.Dimms, &pMemoryResourcesInfo->RawCapacity, &pMemoryResourcesInfo->VolatileCapacity, &pMemoryResourcesInfo->AppDirectCapacity, &pMemoryResourcesInfo->UnconfiguredCapacity, &pMemoryResourcesInfo->ReservedCapacity, &pMemoryResourcesInfo->InaccessibleCapacity)); if (EFI_ERROR(ReturnCode)) { CHECK_RESULT_CONTINUE_PRESERVE_ERROR(CheckIfAllDimmsConfigured(&gNvmDimmData->PMEMDev.Dimms, &pMemoryResourcesInfo->PcdInvalid, NULL)); } // Get DDR Sizes CHECK_RESULT_CONTINUE_PRESERVE_ERROR(GetDDRCapacities(SOCKET_ID_ALL, &pMemoryResourcesInfo->DDRRawCapacity, &pMemoryResourcesInfo->DDRCacheCapacity, &pMemoryResourcesInfo->DDRVolatileCapacity, &pMemoryResourcesInfo->DDRInaccessibleCapacity)); Finish: if (EFI_ERROR(PreservedReturnCode) && !EFI_ERROR(ReturnCode)) { ReturnCode = PreservedReturnCode; } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Gather info about performance on all dimms @param[in] pThis a pointer to EFI_DCPMM_CONFIG2_PROTOCOL instance @param[out] pDimmCount pointer to the number of dimms on list @param[out] pDimmsPerformanceData list of dimms' performance data @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_ABORTED PCAT tables not found @retval Other errors failure of FW commands @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI GetDimmsPerformanceData( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pDimmCount, OUT DIMM_PERFORMANCE_DATA **pDimmsPerformanceData ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; UINT32 Index = 0; PT_OUTPUT_PAYLOAD_MEMORY_INFO_PAGE0 *pPayloadMemInfoPage0 = NULL; PT_OUTPUT_PAYLOAD_MEMORY_INFO_PAGE1 *pPayloadMemInfoPage1 = NULL; NVDIMM_ENTRY(); if ((NULL == pThis) || (NULL == pDimmCount) || (NULL == pDimmsPerformanceData)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Get total DIMM count and allocate memory for the table ReturnCode = GetListSize(&gNvmDimmData->PMEMDev.Dimms, pDimmCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed to get DIMM count; Return code 0x%08x", ReturnCode); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } if(NULL == (*pDimmsPerformanceData = AllocateZeroPool(sizeof(DIMM_PERFORMANCE_DATA) * (*pDimmCount)))) { NVDIMM_ERR("Memory allocation failure"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } LIST_FOR_UNTIL_INDEX(pDimmNode, &gNvmDimmData->PMEMDev.Dimms, *pDimmCount, Index) { pDimm = DIMM_FROM_NODE(pDimmNode); if (!IsDimmManageable(pDimm)) { NVDIMM_WARN("Dimm 0x%x is not manageable", pDimm->DeviceHandle.AsUint32); continue; } (*pDimmsPerformanceData)[Index].DimmId = pDimm->DimmID; // Get Dimm Performance data ReturnCode = FwCmdGetMemoryInfoPage(pDimm, MEMORY_INFO_PAGE_0, sizeof(PT_OUTPUT_PAYLOAD_MEMORY_INFO_PAGE0), (VOID **)&pPayloadMemInfoPage0); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Could not read the memory info page 0; Return code 0x%08x", ReturnCode); ReturnCode = EFI_DEVICE_ERROR; FREE_POOL_SAFE(*pDimmsPerformanceData); goto Finish; } ReturnCode = FwCmdGetMemoryInfoPage(pDimm, MEMORY_INFO_PAGE_1, sizeof(PT_OUTPUT_PAYLOAD_MEMORY_INFO_PAGE1), (VOID **)&pPayloadMemInfoPage1); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Could not read the memory info page 1; Return code 0x%08x", ReturnCode); ReturnCode = EFI_DEVICE_ERROR; FREE_POOL_SAFE(*pDimmsPerformanceData); goto Finish; } // Copy the data (*pDimmsPerformanceData)[Index].MediaReads = pPayloadMemInfoPage0->MediaReads; (*pDimmsPerformanceData)[Index].MediaWrites = pPayloadMemInfoPage0->MediaWrites; (*pDimmsPerformanceData)[Index].ReadRequests = pPayloadMemInfoPage0->ReadRequests; (*pDimmsPerformanceData)[Index].WriteRequests = pPayloadMemInfoPage0->WriteRequests; (*pDimmsPerformanceData)[Index].TotalMediaReads = pPayloadMemInfoPage1->TotalMediaReads; (*pDimmsPerformanceData)[Index].TotalMediaWrites = pPayloadMemInfoPage1->TotalMediaWrites; (*pDimmsPerformanceData)[Index].TotalReadRequests = pPayloadMemInfoPage1->TotalReadRequests; (*pDimmsPerformanceData)[Index].TotalWriteRequests = pPayloadMemInfoPage1->TotalWriteRequests; FREE_POOL_SAFE(pPayloadMemInfoPage0); FREE_POOL_SAFE(pPayloadMemInfoPage1); } Finish: FREE_POOL_SAFE(pPayloadMemInfoPage0); FREE_POOL_SAFE(pPayloadMemInfoPage1); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Parse EFI_ACPI_DESCRIPTION_HEADER (DSDT) and fetch NFIT & PCAT pointers to table Also, parse PMTT table to check if MM can be configured @param[in] pDsdt a pointer to EFI_ACPI_DESCRIPTION_HEADER instance for each of NFIT, PMTT and PCAT @param[out] ppFitHead pointer to pointer to store NFIT table @param[out] ppPcatHead pointer to pointer to store PCAT table @param[out] ppPmttHead pointer to pointer to store PMTT table @param[out] pIsMemoryModeAllowed pointer to check if MM can be configured @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_DEVICE_ERROR could not parse at least one of the tables @retval EFI_SUCCESS Success **/ EFI_STATUS ParseAcpiTables( IN CONST EFI_ACPI_DESCRIPTION_HEADER *pNfit, IN CONST EFI_ACPI_DESCRIPTION_HEADER *pPcat, IN CONST EFI_ACPI_DESCRIPTION_HEADER *pPMTT, OUT ParsedFitHeader **ppFitHead, OUT ParsedPcatHeader **ppPcatHead, OUT ParsedPmttHeader **ppPmttHead, OUT BOOLEAN *pIsMemoryModeAllowed ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); CHECK_NULL_ARG(pNfit, Finish); CHECK_NULL_ARG(pPcat, Finish); // PMTT is optional in some situations, check further down CHECK_NULL_ARG(ppFitHead, Finish); CHECK_NULL_ARG(ppPcatHead, Finish); CHECK_NULL_ARG(ppPmttHead, Finish); CHECK_NULL_ARG(pIsMemoryModeAllowed, Finish); CHECK_RESULT(ParseNfitTable((VOID *)pNfit, ppFitHead), Finish); CHECK_RESULT(ParsePcatTable((VOID *)pPcat, ppPcatHead), Finish); // Assume that PMTT table is not available at first // (skip MM allowed check and let BIOS handle it) *pIsMemoryModeAllowed = TRUE; // If not Purley, a PMTT table is required! Error out if (!IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID((*ppPcatHead)->pPlatformConfigAttr)) { CHECK_NULL_ARG(pPMTT, Finish); } // Only parse a non-NULL PMTT table if (pPMTT != NULL) { CHECK_RESULT(ParsePmttTable((VOID *)pPMTT, ppPmttHead), Finish); // If we didn't fail out in the ParsePmttTable call, then the raw pPMTT table // is not NULL and is a valid revision. Check if memory mode is allowed. *pIsMemoryModeAllowed = CheckIsMemoryModeAllowed((TABLE_HEADER *)pPMTT); } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Fetch the NFIT and PCAT tables from EFI_SYSTEM_TABLE @param[in] pSystemTable is a pointer to the EFI_SYSTEM_TABLE instance @param[out] ppNfit is a pointer to EFI_ACPI_DESCRIPTION_HEADER (NFIT) @param[out] ppPcat is a pointer to EFI_ACPI_DESCRIPTION_HEADER (PCAT) @param[out] ppPMTT is a pointer to EFI_ACPI_DESCRIPTION_HEADER (PMTT) @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER NULL argument @retval EFI_LOAD_ERROR if one or more of the tables could not be found **/ EFI_STATUS GetAcpiTables( IN CONST EFI_SYSTEM_TABLE *pSystemTable, OUT EFI_ACPI_DESCRIPTION_HEADER **ppNfit, OUT EFI_ACPI_DESCRIPTION_HEADER **ppPcat, OUT EFI_ACPI_DESCRIPTION_HEADER **ppPMTT ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *pRsdp = NULL; EFI_ACPI_DESCRIPTION_HEADER *pRsdt = NULL; EFI_ACPI_DESCRIPTION_HEADER *pCurrentTable = NULL; UINT32 Index = 0; UINT64 Tmp = 0; NVDIMM_ENTRY(); if ((pSystemTable == NULL) || (ppNfit == NULL && ppPcat == NULL && ppPMTT == NULL)) { goto Finish; } /** get the Root ACPI table pointer **/ NVDIMM_DBG("Looking for the ACPI Root System Descriptor Pointer (RSDP) in UEFI"); for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { /** ACPI 2.0 **/ if (CompareGuid(&gEfiAcpi20TableGuid, &(gST->ConfigurationTable[Index].VendorGuid))) { NVDIMM_DBG("Found the RSDP table"); pRsdp = gST->ConfigurationTable[Index].VendorTable; break; } } if (pRsdp == NULL) { /** Unable to find the Root ACPI table pointer **/ NVDIMM_WARN("Unable to find the ACPI Root System Descriptor Pointer (RSDP)"); ReturnCode = EFI_LOAD_ERROR; goto Finish; } /** Get the RSDT **/ pRsdt = (EFI_ACPI_DESCRIPTION_HEADER *)pRsdp->XsdtAddress; if (pRsdt == NULL) { NVDIMM_WARN("Unable to find the ACPI Root System Descriptor Table (RSDT)"); ReturnCode = EFI_LOAD_ERROR; goto Finish; } if (NULL != ppNfit) { *ppNfit = NULL; } if (NULL != ppPcat) { *ppPcat = NULL; } if (NULL != ppPMTT) { *ppPMTT = NULL; } /** Find the Fixed ACPI Description Table (FADT) **/ NVDIMM_DBG("Looking for the Fixed ACPI Description Table (FADT)"); for (Index = sizeof (EFI_ACPI_DESCRIPTION_HEADER); Index < pRsdt->Length; Index += sizeof(UINT64)) { Tmp = *(UINT64 *) ((UINT8 *) pRsdt + Index); pCurrentTable = (EFI_ACPI_DESCRIPTION_HEADER *) (UINT64 *) (UINTN) Tmp; if ((NULL != ppNfit) && (pCurrentTable->Signature == NFIT_TABLE_SIG)) { NVDIMM_DBG("Found the NFIT table"); *ppNfit = pCurrentTable; } if ((NULL != ppPcat) && (pCurrentTable->Signature == PCAT_TABLE_SIG)) { NVDIMM_DBG("Found the PCAT table"); *ppPcat = pCurrentTable; } if ((NULL != ppPMTT) && (pCurrentTable->Signature == PMTT_TABLE_SIG)) { NVDIMM_DBG("Found the PMTT table"); *ppPMTT = pCurrentTable; } // Break if we find all tables looking for if (((NULL == ppNfit) || (*ppNfit != NULL)) && ((NULL == ppPcat) || (*ppPcat != NULL)) && ((NULL == ppPMTT) || (*ppPMTT != NULL))) { break; } } /** Failed to find the at least one of the tables **/ if (((NULL != ppNfit) && (NULL == *ppNfit)) || ((NULL != ppPcat) && (NULL == *ppPcat)) || ((NULL != ppPMTT) && (NULL == *ppPMTT))) { NVDIMM_WARN("Unable to find requested ACPI table."); ReturnCode = EFI_LOAD_ERROR; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get the PCI base address from MCFG table from EFI_SYSTEM_TABLE @param[in] pSystemTable is a pointer to the EFI_SYSTEM_TABLE instance @param[out] pPciBaseAddress is a pointer to the Base Address @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER NULL argument @retval EFI_LOAD_ERROR if one or more of the tables could not be found **/ EFI_STATUS GetPciBaseAddress( IN CONST EFI_SYSTEM_TABLE *pSystemTable, OUT UINT64 *pPciBaseAddress ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; #ifndef OS_BUILD EFI_ACPI_MEMORY_MAPPED_ENHANCED_CONFIGURATION_SPACE_BASE_ADDRESS_ALLOCATION_STRUCTURE *pMmConfigStruct; EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *pRsdp = NULL; EFI_ACPI_DESCRIPTION_HEADER *pRsdt = NULL; EFI_ACPI_DESCRIPTION_HEADER *pMcfg = NULL; EFI_ACPI_DESCRIPTION_HEADER *pCurrentTable = NULL; UINT32 Index = 0; UINT64 Tmp = 0; NVDIMM_ENTRY(); if (pSystemTable == NULL || pPciBaseAddress == NULL) { goto Finish; } for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { if (CompareGuid(&gEfiAcpi20TableGuid, &(gST->ConfigurationTable[Index].VendorGuid))) { NVDIMM_DBG("Found the RSDP table"); pRsdp = gST->ConfigurationTable[Index].VendorTable; break; } } if (pRsdp == NULL) { NVDIMM_WARN("Unable to find the ACPI Root System Descriptor Pointer (RSDP)"); ReturnCode = EFI_LOAD_ERROR; goto Finish; } pRsdt = (EFI_ACPI_DESCRIPTION_HEADER *)pRsdp->XsdtAddress; if (pRsdt == NULL) { NVDIMM_WARN("Unable to find the ACPI Root System Descriptor Table (RSDT)"); ReturnCode = EFI_LOAD_ERROR; goto Finish; } for (Index = sizeof (EFI_ACPI_DESCRIPTION_HEADER); Index < pRsdt->Length; Index += sizeof(UINT64)) { Tmp = *(UINT64 *) ((UINT8 *) pRsdt + Index); pCurrentTable = (EFI_ACPI_DESCRIPTION_HEADER *) (UINT64 *) (UINTN) Tmp; if (pCurrentTable->Signature == EFI_ACPI_3_0_PCI_EXPRESS_MEMORY_MAPPED_CONFIGURATION_SPACE_BASE_ADDRESS_DESCRIPTION_TABLE_SIGNATURE) { pMcfg = pCurrentTable; NVDIMM_DBG("Found MCFG table too"); } if (pMcfg != NULL) { break; } } if (pMcfg == NULL) { ReturnCode = EFI_LOAD_ERROR; goto Finish; } pMmConfigStruct = (EFI_ACPI_MEMORY_MAPPED_ENHANCED_CONFIGURATION_SPACE_BASE_ADDRESS_ALLOCATION_STRUCTURE *) ((UINT8 *)pMcfg + sizeof(EFI_ACPI_MEMORY_MAPPED_CONFIGURATION_BASE_ADDRESS_TABLE_HEADER)); if (pMmConfigStruct == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pPciBaseAddress = pMmConfigStruct->BaseAddress; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); #endif return ReturnCode; } /** Check the memory map against the NFIT SPA memory for consistency. @retval EFI_SUCCESS on success @retval EFI_OUT_OF_RESOURCES for a failed allocation @retval EFI_BAD_BUFFER_SIZE if the nfit spa memory is more than the one in memmap **/ EFI_STATUS CheckMemoryMap( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; #ifndef OS_BUILD UINTN Size = 0; EFI_MEMORY_DESCRIPTOR *Buffer = NULL; UINTN MapKey; UINTN ItemSize; UINT32 Version; UINT8 *Walker; UINT64 PhysicalStart = 0; UINT64 SizeFromMemmap = 0; UINT32 Index = 0; BOOLEAN FoundInMemoryMap = FALSE; /** Get the Memory Map. **/ if (gBS->GetMemoryMap(&Size, Buffer, &MapKey, &ItemSize, &Version) == EFI_BUFFER_TOO_SMALL) { Size += SIZE_1KB; Buffer = AllocateZeroPool(Size); if (Buffer == NULL) { NVDIMM_ERR("Failed on allocating the buffer, NULL returned."); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = gBS->GetMemoryMap(&Size, Buffer, &MapKey, &ItemSize, &Version); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Error: Failed to get Memory Map %d", ReturnCode); goto Finish; } } if (Buffer == NULL) { NVDIMM_WARN("GetMemoryMap returned unexpected error."); goto Finish; } ParsedFitHeader *pNFit = gNvmDimmData->PMEMDev.pFitHead; for(Index = 0; Index < pNFit->SpaRangeTblesNum; Index++) { SubTableHeader *pTable = (SubTableHeader *)pNFit->ppSpaRangeTbles[Index]; SpaRangeTbl *pTableSpaRange = (SpaRangeTbl *)pTable; if (CompareMem(&pTableSpaRange->AddressRangeTypeGuid, &gSpaRangePmRegionGuid, sizeof(pTableSpaRange->AddressRangeTypeGuid)) == 0) { FoundInMemoryMap = FALSE; /** Parse through the memory map to find each persistent memory region. **/ for (Walker = (UINT8*)Buffer; Walker < (((UINT8*)Buffer)+Size) && Walker != NULL; Walker += ItemSize){ if (((EFI_MEMORY_DESCRIPTOR*)Walker)->Type == EFI_PERSISTENT_MEMORY_REGION) { PhysicalStart = ((EFI_MEMORY_DESCRIPTOR*)Walker)->PhysicalStart; SizeFromMemmap = ((EFI_MEMORY_DESCRIPTOR*)Walker)->NumberOfPages * SIZE_4KB; if (pTableSpaRange->SystemPhysicalAddressRangeBase >= PhysicalStart && (pTableSpaRange->SystemPhysicalAddressRangeBase + pTableSpaRange->SystemPhysicalAddressRangeLength) <= (PhysicalStart + SizeFromMemmap)) { FoundInMemoryMap = TRUE; break; } } } if (!FoundInMemoryMap) { ReturnCode = EFI_BAD_BUFFER_SIZE; NVDIMM_ERR("Error: Nfit SPA Range not found in Memory Map"); break; } } } Finish: FREE_POOL_SAFE(Buffer); NVDIMM_EXIT_I64(ReturnCode); #endif return ReturnCode; } /** Initialize ACPI tables (NFit and PCAT) @retval EFI_SUCCESS on success @retval EFI_NOT_FOUND Nfit table not found **/ #ifndef OS_BUILD EFI_STATUS initAcpiTables( ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; EFI_ACPI_DESCRIPTION_HEADER *pNfit = NULL; EFI_ACPI_DESCRIPTION_HEADER *pPcat = NULL; EFI_ACPI_DESCRIPTION_HEADER *pPMTT = NULL; UINT64 PciBaseAddress; RETURN_STATUS PcdStatus = RETURN_NOT_STARTED; PbrContext *pContext = PBR_CTX(); NVDIMM_ENTRY(); NVDIMM_DBG("InitAcpiTables\n"); if (PBR_NORMAL_MODE == PBR_GET_MODE(pContext) || PBR_RECORD_MODE == PBR_GET_MODE(pContext)) { /** Find the Differentiated System Description Table (DSDT) from EFI_SYSTEM_TABLE **/ // Ignore errors in retrieving the tables for now. We'll handle them properly // in ParseAcpiTables CHECK_RESULT_CONTINUE(GetAcpiTables(gST, &pNfit, &pPcat, &pPMTT)); } if (PBR_RECORD_MODE == PBR_GET_MODE(pContext)) { if (pNfit) { NVDIMM_DBG("Found NFIT, recording it."); ReturnCode = PbrSetTableRecord(pContext, PBR_RECORD_TYPE_NFIT, pNfit, pNfit->Length); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to record NFIT"); } } if (pPcat) { NVDIMM_DBG("Found PCAT, recording it."); ReturnCode = PbrSetTableRecord(pContext, PBR_RECORD_TYPE_PCAT, pPcat, pPcat->Length); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to record PCAT"); } } if (pPMTT) { NVDIMM_DBG("Found PMTT, recording it."); ReturnCode = PbrSetTableRecord(pContext, PBR_RECORD_TYPE_PMTT, pPMTT, pPMTT->Length); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to record PMTT"); } } } else if (PBR_PLAYBACK_MODE == PBR_GET_MODE(pContext)) { ReturnCode = PbrGetTableRecord(pContext, PBR_RECORD_TYPE_NFIT, (VOID**)&pNfit, (UINT32*)&(pNfit->Length)); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to record NFIT"); } ReturnCode = PbrGetTableRecord(pContext, PBR_RECORD_TYPE_PCAT, (VOID**)&pPcat, (UINT32*)&(pPcat->Length)); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to record PCAT"); } ReturnCode = PbrGetTableRecord(pContext, PBR_RECORD_TYPE_PMTT, (VOID**)&pPMTT, (UINT32*)&(pPMTT->Length)); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to record PMTT"); } } /** Find the MCFG table from the EFI_SYSTEM_TABLE Get the PCI Base Address from the MCFG table **/ ReturnCode = GetPciBaseAddress(gST, &PciBaseAddress); if (EFI_ERROR(ReturnCode) || (PciBaseAddress == 0)) { NVDIMM_WARN("Failed to get PCI Base Address"); goto Finish; } /** Find the NVDIMM FW Interface Table (NFIT) & PCAT **/ CHECK_RESULT(ParseAcpiTables(pNfit, pPcat, pPMTT, &gNvmDimmData->PMEMDev.pFitHead, &gNvmDimmData->PMEMDev.pPcatHead, &gNvmDimmData->PMEMDev.pPmttHead, &gNvmDimmData->PMEMDev.IsMemModeAllowedByBios), Finish); /** Set the Base address **/ PcdStatus = PcdSet64S(PcdPciExpressBaseAddress, PciBaseAddress); if (RETURN_ERROR(PcdStatus)) { NVDIMM_WARN("PcdSet64S failed"); ReturnCode = EFI_LOAD_ERROR; goto Finish; } if(PcdGet64(PcdPciExpressBaseAddress) != PciBaseAddress) { NVDIMM_WARN("Could not set the PCI Base Address"); ReturnCode = EFI_LOAD_ERROR; goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif /** Get System Capabilities information from PCAT and NFIT tables Pointer to variable length pInterleaveFormatsSupported is allocated here and must be freed by caller. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[out] pSysCapInfo is a pointer to table with System Capabilities information @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER NULL argument @retval EFI_NOT_STARTED Pcat tables not parsed **/ EFI_STATUS EFIAPI GetSystemCapabilitiesInfo( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT SYSTEM_CAPABILITIES_INFO *pSysCapInfo ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Index = 0; UINT32 Capabilities = 0; PlatformCapabilitiesTbl *pNfitPlatformCapability = NULL; NVDIMM_ENTRY(); if (pThis == NULL || pSysCapInfo == NULL) { NVDIMM_DBG("Invalid parameter"); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL) { NVDIMM_DBG("Pcat tables not parsed"); ReturnCode = EFI_NOT_STARTED; goto Finish; } if (gNvmDimmData->PMEMDev.pFitHead == NULL) { NVDIMM_DBG("Nfit tables not parsed"); ReturnCode = EFI_NOT_STARTED; goto Finish; } pSysCapInfo->PartitioningAlignment = gNvmDimmData->Alignments.RegionPartitionAlignment; pSysCapInfo->InterleaveSetsAlignment = gNvmDimmData->Alignments.RegionPersistentAlignment; if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr)) { if (gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0] != NULL) { PLATFORM_CAPABILITY_INFO3 *pPlatformCapability3 = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0]; pSysCapInfo->OperatingModeSupport = pPlatformCapability3->MemoryModeCapabilities.MemoryModes; pSysCapInfo->PlatformConfigSupported = pPlatformCapability3->MgmtSwConfigInputSupport; pSysCapInfo->CurrentOperatingMode = pPlatformCapability3->CurrentMemoryMode.MemoryMode; /** Platform capabilities **/ pSysCapInfo->AppDirectMirrorSupported = 0; pSysCapInfo->DimmSpareSupported = 0; pSysCapInfo->AppDirectMigrationSupported = 0; } else { NVDIMM_DBG("Number of Platform Capability Information tables: %d", gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum); ReturnCode = EFI_UNSUPPORTED; goto Finish; } } else { if (gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo[0] != NULL) { PLATFORM_CAPABILITY_INFO *pPlatformCapability = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo[0]; pSysCapInfo->OperatingModeSupport = pPlatformCapability->MemoryModeCapabilities.MemoryModes; pSysCapInfo->PlatformConfigSupported = pPlatformCapability->MgmtSwConfigInputSupport; pSysCapInfo->CurrentOperatingMode = pPlatformCapability->CurrentMemoryMode.MemoryMode; /** Platform capabilities **/ pSysCapInfo->AppDirectMirrorSupported = IS_BIT_SET_VAR(pPlatformCapability->PersistentMemoryRasCapability, BIT0); pSysCapInfo->DimmSpareSupported = IS_BIT_SET_VAR(pPlatformCapability->PersistentMemoryRasCapability, BIT1); pSysCapInfo->AppDirectMigrationSupported = IS_BIT_SET_VAR(pPlatformCapability->PersistentMemoryRasCapability, BIT2); } else { NVDIMM_DBG("Number of Platform Capability Information tables: %d", gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum); ReturnCode = EFI_UNSUPPORTED; goto Finish; } } if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr)) { if (gNvmDimmData->PMEMDev.pPcatHead->MemoryInterleaveCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo[0] != NULL) { MEMORY_INTERLEAVE_CAPABILITY_INFO3 *pInterleaveCapability3 = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo[0]; pSysCapInfo->InterleaveAlignmentSize = pInterleaveCapability3->InterleaveAlignmentSize; pSysCapInfo->InterleaveFormatsSupportedNum = pInterleaveCapability3->NumOfFormatsSupported; pSysCapInfo->PtrInterleaveSize = (HII_POINTER)AllocateZeroPool(sizeof(INTERLEAVE_SIZE)); if ((INTERLEAVE_FORMAT *)pSysCapInfo->PtrInterleaveSize == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } CopyMem_S((INTERLEAVE_SIZE *)pSysCapInfo->PtrInterleaveSize, sizeof(INTERLEAVE_SIZE), &pInterleaveCapability3->InterleaveSize, sizeof(INTERLEAVE_SIZE)); pSysCapInfo->PtrInterleaveFormatsSupported = (HII_POINTER)AllocateZeroPool(sizeof(INTERLEAVE_FORMAT) * pSysCapInfo->InterleaveFormatsSupportedNum); if ((INTERLEAVE_FORMAT *)pSysCapInfo->PtrInterleaveFormatsSupported == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } CopyMem_S((INTERLEAVE_FORMAT *)pSysCapInfo->PtrInterleaveFormatsSupported, sizeof(INTERLEAVE_FORMAT) * pSysCapInfo->InterleaveFormatsSupportedNum, pInterleaveCapability3->InterleaveFormatList, sizeof(INTERLEAVE_FORMAT) * pSysCapInfo->InterleaveFormatsSupportedNum); } } else { if (gNvmDimmData->PMEMDev.pPcatHead->MemoryInterleaveCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo[0] != NULL) { MEMORY_INTERLEAVE_CAPABILITY_INFO *pInterleaveCapability = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo[0]; pSysCapInfo->InterleaveAlignmentSize = pInterleaveCapability->InterleaveAlignmentSize; pSysCapInfo->InterleaveFormatsSupportedNum = pInterleaveCapability->NumOfFormatsSupported; pSysCapInfo->PtrInterleaveSize = 0; pSysCapInfo->PtrInterleaveFormatsSupported = (HII_POINTER)AllocateZeroPool(sizeof(INTERLEAVE_FORMAT) * pSysCapInfo->InterleaveFormatsSupportedNum); if ((INTERLEAVE_FORMAT *)pSysCapInfo->PtrInterleaveFormatsSupported == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } CopyMem_S((INTERLEAVE_FORMAT *)pSysCapInfo->PtrInterleaveFormatsSupported, sizeof(INTERLEAVE_FORMAT) * pSysCapInfo->InterleaveFormatsSupportedNum, pInterleaveCapability->InterleaveFormatList, sizeof(INTERLEAVE_FORMAT) * pSysCapInfo->InterleaveFormatsSupportedNum); } } if (gNvmDimmData->PMEMDev.pFitHead->PlatformCapabilitiesTblesNum == 1 && gNvmDimmData->PMEMDev.pFitHead->ppPlatformCapabilitiesTbles != NULL && gNvmDimmData->PMEMDev.pFitHead->ppPlatformCapabilitiesTbles[0] != NULL) { pNfitPlatformCapability = gNvmDimmData->PMEMDev.pFitHead->ppPlatformCapabilitiesTbles[0]; Capabilities = pNfitPlatformCapability->Capabilities & NFIT_MEMORY_CONTROLLER_FLUSH_BIT1; pSysCapInfo->AdrSupported = (Capabilities == NFIT_MEMORY_CONTROLLER_FLUSH_BIT1); } for (Index = 0; Index < SUPPORTED_BLOCK_SIZES_COUNT; Index++) { CopyMem_S(&pSysCapInfo->NsBlockSizes[Index], sizeof(pSysCapInfo->NsBlockSizes[Index]), &gSupportedBlockSizes[Index], sizeof(pSysCapInfo->NsBlockSizes[Index])); } pSysCapInfo->MinNsSize = gNvmDimmData->Alignments.PmNamespaceMinSize; /** Features supported by the driver **/ pSysCapInfo->RenameNsSupported = FEATURE_NOT_SUPPORTED; /** The UEFI driver does not support this feature **/ pSysCapInfo->GrowPmNsSupported = FEATURE_NOT_SUPPORTED; pSysCapInfo->ShrinkPmNsSupported = FEATURE_NOT_SUPPORTED; pSysCapInfo->GrowBlkNsSupported = FEATURE_NOT_SUPPORTED; pSysCapInfo->ShrinkBlkNsSupported = FEATURE_NOT_SUPPORTED; pSysCapInfo->InitiateScrubSupported = FEATURE_NOT_SUPPORTED; #ifdef OS_BUILD pSysCapInfo->EraseDeviceDataSupported = FEATURE_NOT_SUPPORTED; pSysCapInfo->EnableDeviceSecuritySupported = FEATURE_NOT_SUPPORTED; pSysCapInfo->DisableDeviceSecuritySupported = FEATURE_NOT_SUPPORTED; pSysCapInfo->UnlockDeviceSecuritySupported = FEATURE_NOT_SUPPORTED; pSysCapInfo->FreezeDeviceSecuritySupported = FEATURE_NOT_SUPPORTED; pSysCapInfo->ChangeDevicePassphraseSupported = FEATURE_NOT_SUPPORTED; pSysCapInfo->MasterEraseDeviceDataSupported = FEATURE_NOT_SUPPORTED; pSysCapInfo->ChangeMasterPassphraseSupported = FEATURE_NOT_SUPPORTED; #else pSysCapInfo->EraseDeviceDataSupported = FEATURE_SUPPORTED; pSysCapInfo->EnableDeviceSecuritySupported = FEATURE_SUPPORTED; pSysCapInfo->DisableDeviceSecuritySupported = FEATURE_SUPPORTED; pSysCapInfo->UnlockDeviceSecuritySupported = FEATURE_SUPPORTED; pSysCapInfo->FreezeDeviceSecuritySupported = FEATURE_SUPPORTED; pSysCapInfo->ChangeDevicePassphraseSupported = FEATURE_SUPPORTED; pSysCapInfo->MasterEraseDeviceDataSupported = FEATURE_SUPPORTED; pSysCapInfo->ChangeMasterPassphraseSupported = FEATURE_SUPPORTED; #endif Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } EFI_STATUS ValidateImageVersion( IN NVM_FW_IMAGE_HEADER *pImage, IN BOOLEAN Force, IN DIMM *pDimm, OUT NVM_STATUS *pNvmStatus, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM_BSR Bsr; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; CHAR16 *pDimmSteppingStr = NULL; CHAR16 *pImgSteppingStr = NULL; NVDIMM_ENTRY(); if (pImage == NULL || pDimm == NULL || pNvmStatus == NULL) { goto Finish; } *pNvmStatus = NVM_SUCCESS; //assume success ZeroMem(&Bsr, sizeof(Bsr)); ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (pDimm->FwVer.FwProduct == 0) { // Dimm seems inaccessible over DDRT and SMBUS, as we couldn't get the proper // firmware version *pNvmStatus = NVM_ERR_TIMEOUT; ReturnCode = EFI_ABORTED; goto Finish; } if (pDimm->FwVer.FwProduct != pImage->ImageVersion.ProductNumber.Version) { *pNvmStatus = NVM_ERR_FIRMWARE_VERSION_NOT_VALID; CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_PRODUCT_NUMBER_MISMATCH); ReturnCode = EFI_ABORTED; goto Finish; } if (!Force && pDimm->FwVer.FwRevision > pImage->ImageVersion.RevisionNumber.Version) { *pNvmStatus = NVM_ERR_FIRMWARE_VERSION_NOT_VALID; CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_REVISION_NUMBER_MISMATCH); ReturnCode = EFI_ABORTED; goto Finish; } if (pDimm->FwVer.FwSecurityVersion > pImage->ImageVersion.SecurityRevisionNumber.Version) { ReturnCode = pNvmDimmConfigProtocol->GetBSRAndBootStatusBitMask(pNvmDimmConfigProtocol, pDimm->DimmID, &Bsr.AsUint64, NULL); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Could not get the DIMM BSR register, can't check if it is safe to send the command."); ReturnCode = EFI_ABORTED; goto Finish; } if (Bsr.Separated_Current_FIS.SVNDE != DIMM_BSR_SVNDE_ENABLED) { *pNvmStatus = NVM_ERR_FIRMWARE_VERSION_NOT_VALID; CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_SVNDE_NOT_ENABLED); ReturnCode = EFI_ABORTED; goto Finish; } if (!Force) { *pNvmStatus = NVM_ERR_FIRMWARE_TOO_LOW_FORCE_REQUIRED; ReturnCode = EFI_ABORTED; goto Finish; } } if (pDimm->FwVer.FwRevision == pImage->ImageVersion.RevisionNumber.Version && pDimm->FwVer.FwSecurityVersion == pImage->ImageVersion.SecurityRevisionNumber.Version && pDimm->FwVer.FwBuild > pImage->ImageVersion.BuildNumber.Build) { if (!Force) { *pNvmStatus = NVM_ERR_FIRMWARE_TOO_LOW_FORCE_REQUIRED; ReturnCode = EFI_ABORTED; goto Finish; } } if (((BCD_TO_TWO_DEC(pImage->FwApiVersion.Byte.Digit1) < MIN_FIS_SUPPORTED_BY_THIS_SW_MAJOR) || (BCD_TO_TWO_DEC(pImage->FwApiVersion.Byte.Digit1) == MIN_FIS_SUPPORTED_BY_THIS_SW_MAJOR && BCD_TO_TWO_DEC(pImage->FwApiVersion.Byte.Digit2) < MIN_FIS_SUPPORTED_BY_THIS_SW_MINOR)) || (BCD_TO_TWO_DEC(pImage->FwApiVersion.Byte.Digit1) > MAX_FIS_SUPPORTED_BY_THIS_SW_MAJOR)) { *pNvmStatus = NVM_ERR_FIRMWARE_API_NOT_VALID; ReturnCode = EFI_ABORTED; goto Finish; } if ((pDimm->ControllerRid != pImage->RevisionId)) { *pNvmStatus = NVM_ERR_IMAGE_FILE_NOT_COMPATIBLE_TO_CTLR_STEPPING; pDimmSteppingStr = ControllerRidToStr(pDimm->ControllerRid, pDimm->SubsystemDeviceId); pImgSteppingStr = ControllerRidToStr(pImage->RevisionId, pImage->DeviceId); CatSPrintNCopy(pCommandStatus->StatusDetails, MAX_STATUS_DETAILS_STR_LEN, DETAILS_CANT_USE_IMAGE, pImgSteppingStr, pDimmSteppingStr); ReturnCode = EFI_ABORTED; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /* * Helper function for writing a spi image to a backup file. * Does not overwrite an existing file */ EFI_STATUS DebugWriteSpiImageToFile( IN CHAR16 *pWorkingDirectory OPTIONAL, IN UINT32 DimmHandle, IN CONST VOID *pSpiImageBuffer, IN UINT64 ImageBufferSize ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_FILE_HANDLE FileHandle = NULL; CHAR16 *FileNameFconfig = NULL; FileHandle = NULL; FileNameFconfig = CatSPrint(NULL, L"new_spi_w_merged_fconfig_0x%04x.bin", DimmHandle); // Do not overwrite if file exists ReturnCode = OpenFileBinary(FileNameFconfig, &FileHandle, pWorkingDirectory, FALSE); if (ReturnCode != EFI_NOT_FOUND) { ReturnCode = EFI_WRITE_PROTECTED; NVDIMM_ERR("Found existing file when trying to write backup file %s", FileNameFconfig); NVDIMM_ERR("Move the existing file to a safe location, as it likely has the original fconfigs"); goto Finish; } // If a handle was opened for some reason, make sure to close it if (FileHandle != NULL) { FileHandle->Close(FileHandle); } ReturnCode = OpenFileBinary(FileNameFconfig, &FileHandle, pWorkingDirectory, TRUE); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = FileHandle->Write(FileHandle, &ImageBufferSize, (VOID *)pSpiImageBuffer); if (EFI_ERROR(ReturnCode)) { goto Finish; } Finish: FREE_POOL_SAFE(FileNameFconfig); if (FileHandle != NULL) { FileHandle->Close(FileHandle); } return ReturnCode; } /** Flash new SPI image to a specified DCPMM @param[in] DimmPid Dimm ID of a DCPMM on which recovery is to be performed @param[in] pNewSpiImageBuffer is a pointer to new SPI FW image @param[in] ImageBufferSize is SPI image size in bytes @param[out] pNvmStatus NVM error code @param[out] pCommandStatus command status list @retval EFI_INVALID_PARAMETER One of parameters provided is not acceptable @retval EFI_NOT_FOUND there is no DCPMM with such Pid @retval EFI_DEVICE_ERROR Unable to communicate with Dimm SPI @retval EFI_OUT_OF_RESOURCES Unable to allocate memory for a data structure @retval EFI_ACCESS_DENIED When SPI access is not unlocked @retval EFI_SUCCESS Update has completed successfully **/ EFI_STATUS EFIAPI RecoverDimmFw( IN UINT32 DimmHandle, IN CONST VOID *pNewSpiImageBuffer, IN UINT64 ImageBufferSize, IN CHAR16 *pWorkingDirectory OPTIONAL, OUT NVM_STATUS *pNvmStatus, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; #ifndef OS_BUILD DIMM *pCurrentDimm = NULL; EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; NVM_SPI_DIRECTORY_GEN2 *pSpiDirectoryNewSpiImageBuffer; NVM_SPI_DIRECTORY_GEN2 SpiDirectoryTarget; UINT8 *pFconfigRegionNewSpiImageBuffer = NULL; UINT8 *pFconfigRegionTemp = NULL; UINT16 DeviceId; NVDIMM_ENTRY(); if (pNewSpiImageBuffer == NULL || pCommandStatus == NULL || pNvmStatus == NULL) { goto Finish; } pSpiDirectoryNewSpiImageBuffer = (NVM_SPI_DIRECTORY_GEN2 *) pNewSpiImageBuffer; ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **) &pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { goto Finish; } pCurrentDimm = GetDimmByHandle(DimmHandle, &gNvmDimmData->PMEMDev.Dimms); if (pCurrentDimm == NULL) { NVDIMM_ERR("Failed to find handle 0x%x in dimm list", DimmHandle); *pNvmStatus = NVM_ERR_DIMM_NOT_FOUND; goto Finish; } ReturnCode = SpiFlashAccessible(pCurrentDimm); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Spi access is not enabled on DIMM 0x%x", pCurrentDimm->DeviceHandle.AsUint32); *pNvmStatus = NVM_ERR_SPI_ACCESS_NOT_ENABLED; goto Finish; } // Make sure we are working with a DCPMM 1st gen device ReturnCode = GetDeviceIdSpd(pCurrentDimm->SmbusAddress, &DeviceId); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Cannot access spd data over smbus: 0x%x", ReturnCode); *pNvmStatus = NVM_ERR_SPD_NOT_ACCESSIBLE; goto Finish; } if (DeviceId != SPD_DEVICE_ID_15) { NVDIMM_ERR("Incompatible hardware revision 0x%x", DeviceId); *pNvmStatus = NVM_ERR_INCOMPATIBLE_HARDWARE_REVISION; goto Finish; } pFconfigRegionTemp = (UINT8 *)AllocateZeroPool(SPI_FCONFIG_REGION_MAX_SIZE_BYTES); if (pFconfigRegionTemp == NULL) { NVDIMM_ERR("Out of memory"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } // Gather fconfig data ReturnCode = ReadSpiDirectoryTarget(pCurrentDimm, &SpiDirectoryTarget); if (!EFI_ERROR(ReturnCode)) { ReturnCode = ReadAndVerifyFconfigTarget(pCurrentDimm, &SpiDirectoryTarget, pFconfigRegionTemp); } // If there are any errors in recovering configuration data from the existing module spi image, // we need to recreate the fconfig data by merging entries from the spd // and the new spi image fconfig. Copy them to pFconfigRegionTemp if (EFI_ERROR(ReturnCode)) { ZeroMem(pFconfigRegionTemp, SPI_FCONFIG_REGION_MAX_SIZE_BYTES); ReturnCode = RecreateFconfigFromSpdAndNewSpiImage((UINT8 *)pNewSpiImageBuffer, ImageBufferSize, pCurrentDimm, pFconfigRegionTemp); if (EFI_ERROR(ReturnCode)) { goto Finish; } } // Copy original fconfig or generated fconfig to the new image pFconfigRegionNewSpiImageBuffer = ((UINT8 *)pNewSpiImageBuffer + pSpiDirectoryNewSpiImageBuffer->FconfigDataOffset); CopyMem((VOID *)pFconfigRegionNewSpiImageBuffer, (VOID *)pFconfigRegionTemp, sizeof(FconfigContainerHeader) + ((FconfigContainerHeader *)pFconfigRegionTemp)->DwordLen * sizeof(UINT32)); // Copy migration data from the existing dimm spi image, if applicable CHECK_RESULT(ReadAndCopyMigrationData(pCurrentDimm, &SpiDirectoryTarget, pSpiDirectoryNewSpiImageBuffer), Finish); //////////////////////////////////////////////////// // Write out the new image to a file in order to recover the // dimm back to a good state after testing. // TODO: Needed for validation only! We do not need this after product PRQ and will // probably confuse people DebugWriteSpiImageToFile(pWorkingDirectory, pCurrentDimm->DeviceHandle.AsUint32, pNewSpiImageBuffer, ImageBufferSize); /////////////////////////////////////////////////// CHECK_RESULT(SpiEraseChip(pCurrentDimm, pCommandStatus), Finish); CHECK_RESULT(SpiWrite(pCurrentDimm, pNewSpiImageBuffer, (UINT32)ImageBufferSize, SPI_START_ADDRESS, FALSE, pCommandStatus), Finish); Finish: FREE_POOL_SAFE(pFconfigRegionTemp); NVDIMM_EXIT_I64(ReturnCode); #endif return ReturnCode; } /** Update firmware or training data in one or all NVDIMMs of the system @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds is a pointer to an array of DIMM IDs - if NULL, execute operation on all dimms @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[in] pFileName Name is a pointer to a file containing FW image @param[in] pWorkingDirectory is a pointer to a path to FW image file @param[in] Examine flag enables image verification only @param[in] Force flag suppresses warning message in case of attempted downgrade @param[in] Recovery flag determine that recovery update should be performed @param[in] Reserved Set to FALSE @param[out] pFwImageInfo is a pointer to a structure containing FW image information need to be provided if examine flag is set @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER One of parameters provided is not acceptable @retval EFI_NOT_FOUND there is no NVDIMM with such Pid @retval EFI_OUT_OF_RESOURCES Unable to allocate memory for a data structure @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system @retval EFI_SUCCESS Update has completed successfully **/ EFI_STATUS EFIAPI UpdateFw( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN CHAR16 *pFileName, IN CHAR16 *pWorkingDirectory OPTIONAL, IN BOOLEAN Examine, IN BOOLEAN Force, IN BOOLEAN Recovery, IN BOOLEAN Reserved, OUT NVM_FW_IMAGE_INFO *pFwImageInfo OPTIONAL, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; UINT32 Index = 0; UINT64 FWImageMaxSize = 0; NVM_FW_IMAGE_HEADER *pFileHeader = NULL; EFI_FILE_HANDLE FileHandle = NULL; VOID *pImageBuffer = NULL; CHAR16 *pErrorMessage = NULL; UINTN BuffSize = 0; UINTN TempBuffSize = 0; NVM_STATUS NvmStatus = NVM_ERR_OPERATION_NOT_STARTED; BOOLEAN* pDimmsCanBeUpdated = NULL; UINT32 DimmsToUpdate = 0; UINT32 UpdateFailures = 0; UINT32 VerificationFailures = 0; UINT32 ForceRequiredDimms = 0; UINT16 SubsystemDeviceId = 0x0; REQUIRE_DCPMMS RequireDcpmmsBitfield = REQUIRE_DCPMMS_MANAGEABLE; // FlashSPI is unsupported. Will remove more completely in a future change BOOLEAN FlashSPI = FALSE; EFI_STATUS LongOpStatusReturnCode = 0; NVM_STATUS LongOpNvmStatus = NVM_ERR_OPERATION_NOT_STARTED; ZeroMem(pDimms, sizeof(pDimms)); NVDIMM_ENTRY(); if (pCommandStatus == NULL) { return EFI_INVALID_PARAMETER; } CHECK_RESULT(BlockMixedSku(pCommandStatus), Finish); if (pThis == NULL || (pDimmIds == NULL && DimmIdsCount > 0)) { pCommandStatus->GeneralStatus = NVM_ERR_INVALID_PARAMETER; goto Finish; } pCommandStatus->GeneralStatus = NvmStatus; if (pFileName == NULL) { pCommandStatus->GeneralStatus = NVM_ERR_FILENAME_NOT_PROVIDED; goto Finish; } if (Reserved == TRUE) { pCommandStatus->GeneralStatus = NVM_ERR_FLASH_SPI_NO_LONGER_SUPPORTED; goto Finish; } if (!Recovery && FlashSPI) { pCommandStatus->GeneralStatus = NVM_ERR_INVALID_PARAMETER; goto Finish; } if (Recovery && FlashSPI) { // In FlashSPI scenario. // For now, keep existing behavior of skipping the standard VerifyTargetDimms() ReturnCode = VerifyNonfunctionalTargetDimms(pDimmIds, DimmIdsCount, pDimms, &DimmsNum, pCommandStatus); } else { if (Recovery) { // Not FlashSPI. // For backwards compatibility, keep "-recover" meaning // "only run on non-functional DCPMMs". Reject functional DCPMMs. RequireDcpmmsBitfield |= REQUIRE_DCPMMS_NON_FUNCTIONAL; } // Note: By default non-functional DCPMMs are included in normal firmware // update (don't require "-recover" to be passed in) ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, NULL, 0, RequireDcpmmsBitfield, pDimms, &DimmsNum, pCommandStatus); } if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed to verify the target dimms"); pCommandStatus->GeneralStatus = NVM_ERR_DIMM_NOT_FOUND; goto Finish; } ReturnCode = OpenFileBinary(pFileName, &FileHandle, pWorkingDirectory, FALSE); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("OpenFile returned: " FORMAT_EFI_STATUS ".\n", ReturnCode); pCommandStatus->GeneralStatus = NVM_ERR_FILE_NOT_FOUND; goto Finish; } FileHandle->Close(FileHandle); FileHandle = NULL; // find the device id. Must be same for all Dimms if (DimmsNum > 0) { SubsystemDeviceId = pDimms[0]->SubsystemDeviceId; for (Index = 1; Index < DimmsNum; ++Index) { if (SubsystemDeviceId != pDimms[Index]->SubsystemDeviceId) { NVDIMM_DBG("Dimms with different subsystem device ids are not allowed."); pCommandStatus->GeneralStatus = NVM_ERR_MIXED_GENERATIONS_NOT_SUPPORTED; goto Finish; } } } FWImageMaxSize = GetMinFWImageMaxSize(pThis, pDimms, DimmsNum); if (FWImageMaxSize == MAX_UINT64){ NVDIMM_DBG("GetMinFWImageMaxSize failed, maximum allowed firmware image size was not specified"); goto Finish; } if (!LoadFileAndCheckHeader(pFileName, pWorkingDirectory, FlashSPI, SubsystemDeviceId, FWImageMaxSize, &pFileHeader, pCommandStatus)) { for (Index = 0; Index < DimmsNum; Index++) { VerificationFailures++; SetObjStatusForDimmWithErase(pCommandStatus, pDimms[Index], NVM_ERR_IMAGE_FILE_NOT_VALID, TRUE); } NVDIMM_DBG("LoadFileAndCheckHeader Failed"); goto Finish; } if (pFwImageInfo != NULL) { pFwImageInfo->Date = pFileHeader->Date; pFwImageInfo->ImageVersion = pFileHeader->ImageVersion; pFwImageInfo->FirmwareType = pFileHeader->ImageType; pFwImageInfo->ModuleVendor = pFileHeader->ModuleVendor; pFwImageInfo->Size = pFileHeader->Size; } ReturnCode = OpenFileBinary(pFileName, &FileHandle, pWorkingDirectory, FALSE); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed to open the file"); pCommandStatus->GeneralStatus = NVM_ERR_UNKNOWN; goto Finish; } ReturnCode = GetFileSize(FileHandle, &BuffSize); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("Failed get the file size"); pCommandStatus->GeneralStatus = NVM_ERR_UNKNOWN; goto Finish; } pImageBuffer = AllocateZeroPool(BuffSize); if (pImageBuffer == NULL) { NVDIMM_ERR("Out of memory"); pCommandStatus->GeneralStatus = NVM_ERR_UNKNOWN; goto Finish; } TempBuffSize = BuffSize; ReturnCode = FileHandle->Read(FileHandle, &BuffSize, pImageBuffer); if (EFI_ERROR(ReturnCode) || BuffSize != TempBuffSize) { if (Examine) { pCommandStatus->GeneralStatus = NVM_ERR_IMAGE_EXAMINE_INVALID; } else { pCommandStatus->GeneralStatus = NVM_ERR_IMAGE_FILE_NOT_VALID; } goto Finish; } pDimmsCanBeUpdated = AllocatePool(sizeof(BOOLEAN) * DimmsNum); if (pDimmsCanBeUpdated == NULL) { NVDIMM_ERR("Out of memory"); pCommandStatus->GeneralStatus = NVM_ERR_NO_MEM; goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { pDimmsCanBeUpdated[Index] = FALSE; if (Recovery && FlashSPI) { //We will only be able to flash spi if we can access the //spi interface over smbus #ifdef OS_BUILD // Spi check access will fail with unsupported on OS SetObjStatusForDimmWithErase(pCommandStatus, pDimms[Index], NVM_ERR_SPI_ACCESS_NOT_ENABLED, TRUE); VerificationFailures++; #else ReturnCode = SpiFlashAccessible(pDimms[Index]); if (EFI_ERROR(ReturnCode)) { SetObjStatusForDimmWithErase(pCommandStatus, pDimms[Index], NVM_ERR_SPI_ACCESS_NOT_ENABLED, TRUE); VerificationFailures++; } else { pDimmsCanBeUpdated[Index] = TRUE; SetObjStatusForDimmWithErase(pCommandStatus, pDimms[Index], NVM_SUCCESS_IMAGE_EXAMINE_OK, TRUE); } #endif } else { ReturnCode = ValidateImageVersion(pFileHeader, Force, pDimms[Index], &NvmStatus, pCommandStatus); if (EFI_ERROR(ReturnCode)) { VerificationFailures++; pCommandStatus->GeneralStatus = NvmStatus; if (ReturnCode == EFI_ABORTED) { if (NvmStatus == NVM_ERR_FIRMWARE_TOO_LOW_FORCE_REQUIRED) { SetObjStatusForDimmWithErase(pCommandStatus, pDimms[Index], NVM_ERR_IMAGE_EXAMINE_LOWER_VERSION, TRUE); ForceRequiredDimms++; } else { SetObjStatusForDimmWithErase(pCommandStatus, pDimms[Index], NvmStatus, TRUE); } } } else { pDimmsCanBeUpdated[Index] = TRUE; SetObjStatusForDimmWithErase(pCommandStatus, pDimms[Index], NVM_SUCCESS_IMAGE_EXAMINE_OK, TRUE); } } } if (TRUE == Examine) { if (VerificationFailures == 0) { pCommandStatus->GeneralStatus = NVM_SUCCESS; } goto Finish; } //count up the number of DIMMs to update DimmsToUpdate = 0; for (Index = 0; Index < DimmsNum; Index++) { if (pDimmsCanBeUpdated[Index] == TRUE) { DimmsToUpdate++; } } if (DimmsToUpdate == 0) { if (pCommandStatus->GeneralStatus == NVM_ERR_OPERATION_NOT_STARTED) { NVDIMM_DBG("Found no DIMMs to update - either none were passed or none passed the verification checks"); pCommandStatus->GeneralStatus = NVM_ERR_DIMM_NOT_FOUND; } goto Finish; } // upload FW image to all specified DIMMs for (Index = 0; Index < DimmsNum; Index++) { if (pDimmsCanBeUpdated[Index] == FALSE) { NVDIMM_DBG("Skipping dimm %d. It is marked as not being currently capable of this update", pDimms[Index]->DeviceHandle.AsUint32); continue; } if (Recovery && FlashSPI) { ReturnCode = RecoverDimmFw(pDimms[Index]->DeviceHandle.AsUint32, pImageBuffer, BuffSize, pWorkingDirectory, &NvmStatus, pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_ERR("RecoverDimmFw returned: " FORMAT_EFI_STATUS ".\n", ReturnCode); } } else { ReturnCode = FwCmdUpdateFw(pDimms[Index], pImageBuffer, BuffSize, &NvmStatus, pCommandStatus); } if (ReturnCode != EFI_SUCCESS) { UpdateFailures++; //Perform a check to see if it was a long operation that blocked the update and get more details about it LongOpStatusReturnCode = CheckForLongOpStatusInProgress(pDimms[Index], &LongOpNvmStatus); if (LongOpStatusReturnCode == EFI_SUCCESS && LongOpNvmStatus != NVM_SUCCESS) { pCommandStatus->GeneralStatus = LongOpNvmStatus; SetObjStatusForDimmWithErase(pCommandStatus, pDimms[Index], LongOpNvmStatus, TRUE); } else { if (NvmStatus == NVM_SUCCESS) { pCommandStatus->GeneralStatus = NVM_ERR_OPERATION_FAILED; SetObjStatusForDimmWithErase(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED, TRUE); } else { pCommandStatus->GeneralStatus = NvmStatus; SetObjStatusForDimmWithErase(pCommandStatus, pDimms[Index], NvmStatus, TRUE); } } } else { SetObjStatusForDimmWithErase(pCommandStatus, pDimms[Index], NvmStatus, TRUE); } } if (0 == UpdateFailures) { pCommandStatus->GeneralStatus = NVM_SUCCESS; } Finish: if (ForceRequiredDimms > 0 && !Force) { pCommandStatus->GeneralStatus = NVM_ERR_FIRMWARE_TOO_LOW_FORCE_REQUIRED; } ReturnCode = EFI_SUCCESS; if (pCommandStatus->GeneralStatus != NVM_SUCCESS) { ReturnCode = EFI_ABORTED; } if (FileHandle != NULL) { FileHandle->Close(FileHandle); } FREE_POOL_SAFE(pFileHeader); FREE_POOL_SAFE(pImageBuffer); FREE_POOL_SAFE(pErrorMessage); FREE_POOL_SAFE(pDimmsCanBeUpdated); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Examine a given DIMM to see if a long op is in progress and report it back @param[in] pDimm The dimm to check the status of @param[out] pNvmStatus The status of the dimm's long op status. NVM_SUCCESS = No long op status is under way. @retval EFI_SUCCESS if the request for long op status was successful (whether a long op status is under way or not) @retval EFI_... the error preventing the check for the long op status **/ EFI_STATUS CheckForLongOpStatusInProgress( IN DIMM *pDimm, OUT NVM_STATUS *pNvmStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT8 LongOpStatusCode = 0; PT_OUTPUT_PAYLOAD_FW_LONG_OP_STATUS LongOpStatus; EFI_STATUS LongOpCheckRetCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (pNvmStatus == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pNvmStatus = NVM_SUCCESS; LongOpCheckRetCode = FwCmdGetLongOperationStatus(pDimm, &LongOpStatusCode, &LongOpStatus); if (EFI_ERROR(LongOpCheckRetCode)) { //fatal error *pNvmStatus = NVM_ERR_UNKNOWN; ReturnCode = LongOpCheckRetCode; goto Finish; } else if (LongOpStatus.Status != FW_DEVICE_BUSY) { //no long op in progress goto Finish; } if (LongOpStatus.CmdOpcode == PtSetFeatures && LongOpStatus.CmdSubOpcode == SubopAddressRangeScrub) { *pNvmStatus = NVM_ERR_ARS_IN_PROGRESS; } else if (LongOpStatus.CmdOpcode == PtUpdateFw && LongOpStatus.CmdSubOpcode == SubopUpdateFw) { *pNvmStatus = NVM_ERR_FWUPDATE_IN_PROGRESS; } else if (LongOpStatus.CmdOpcode == PtSetSecInfo && LongOpStatus.CmdSubOpcode == SubopOverwriteDimm) { *pNvmStatus = NVM_ERR_OVERWRITE_DIMM_IN_PROGRESS; } else { *pNvmStatus = NVM_ERR_UNKNOWN_LONG_OP_IN_PROGRESS; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Filter a list of dimms by a given socket and return an array of dimms that exist on a given socket @param[in] SocketId Socket to retrieve dimms for @param[in] pDimms Array of pointers to manageable DIMMs only @param[in] DimmsNum Number of pointers in pDimms @param[out] pDimmsOnSocket Array of Dimms @param[out] pNumberDimmsOnSocket Returned number of items @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER if one or more parameters are NULL **/ STATIC EFI_STATUS FilterDimmBySocket( IN UINT32 SocketId, IN DIMM *pDimms[MAX_DIMMS], IN UINT32 DimmsNum, OUT DIMM *pDimmsOnSocket[MAX_DIMMS], OUT UINT32 *pNumberDimmsOnSocket ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Index = 0; NVDIMM_ENTRY(); if (pDimms == NULL || pDimmsOnSocket == NULL || pNumberDimmsOnSocket == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pNumberDimmsOnSocket = 0; ZeroMem(pDimmsOnSocket, sizeof(pDimmsOnSocket[0]) * MAX_DIMMS); for (Index = 0; Index < DimmsNum; Index++) { if (pDimms[Index]->SocketId == SocketId) { pDimmsOnSocket[*pNumberDimmsOnSocket] = pDimms[Index]; (*pNumberDimmsOnSocket)++; } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get actual Region goal capacities that would be used based on input values. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of DIMM IDs @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[in] PersistentMemType Persistent memory type @param[in, out] pVolatilePercent Volatile region size in percents. @param[in] ReservedPercent Amount of AppDirect memory to not map in percents @param[in] ReserveDimm Reserve one DIMM for use as a not interleaved AppDirect memory @param[out] pConfigGoals pointer to output array @param[out] pConfigGoalsCount number of elements written @param[out] pNumOfDimmsTargeted number of DIMMs targeted in a goal config request @param[out] pMaxPMInterleaveSetsPerDie pointer to Maximum PM Interleave Sets per Die @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All Ok **/ EFI_STATUS EFIAPI GetActualRegionsGoalCapacities( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds OPTIONAL, IN UINT32 SocketIdsCount, IN UINT8 PersistentMemType, IN OUT UINT32 *pVolatilePercent, IN UINT32 ReservedPercent, IN UINT8 ReserveDimm, OUT REGION_GOAL_PER_DIMM_INFO *pConfigGoals, OUT UINT32 *pConfigGoalsCount, OUT UINT32 *pNumOfDimmsTargeted OPTIONAL, OUT UINT32 *pMaxPMInterleaveSetsPerDie OPTIONAL, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM **ppDimms = NULL; UINT32 DimmsNum = 0; REGION_GOAL_DIMM *pDimmsSymPerSocket = NULL; UINT32 DimmsSymNumPerSocket = 0; REGION_GOAL_DIMM *pDimmsAsymPerSocket = NULL; UINT32 DimmsAsymNumPerSocket = 0; DIMM *pReserveDimm = NULL; UINT64 ActualVolatileSize = 0; REGION_GOAL_TEMPLATE RegionGoalTemplates[MAX_IS_PER_DIMM]; UINT32 RegionGoalTemplatesNum = 0; UINT32 Index = 0; UINT32 Index2 = 0; BOOLEAN Found = FALSE; MEMORY_MODE AllowedMode = MEMORY_MODE_1LM; UINT32 DimmSecurityStateMask = 0; DIMM *pDimmsOnSocket[MAX_DIMMS]; UINT32 NumDimmsOnSocket = 0; UINT64 TotalInputVolatileSize = 0; UINT64 TotalActualVolatileSize = 0; UINT32 Socket = 0; REGION_GOAL_DIMM *pDimmsSym = NULL; UINT32 DimmsSymNum = 0; REGION_GOAL_DIMM *pDimmsAsym = NULL; UINT32 DimmsAsymNum = 0; MAX_PMINTERLEAVE_SETS MaxPMInterleaveSets; ACPI_REVISION PcatRevision; BOOLEAN IsDimmUnlocked = FALSE; REQUIRE_DCPMMS RequireDcpmmsBitfield = REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_FUNCTIONAL | REQUIRE_DCPMMS_MEDIA_ACCESSIBLE; NVDIMM_ENTRY(); ZeroMem(RegionGoalTemplates, sizeof(RegionGoalTemplates)); ZeroMem(&MaxPMInterleaveSets, sizeof(MaxPMInterleaveSets)); ZeroMem(&PcatRevision, sizeof(PcatRevision)); if (pThis == NULL || pCommandStatus == NULL || pVolatilePercent == NULL || pConfigGoals == NULL || pConfigGoalsCount == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pConfigGoalsCount = 0; if (gNvmDimmData->PMEMDev.pPcatHead != NULL) { PcatRevision.AsUint8 = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr->Header.Revision.AsUint8; } ppDimms = AllocateZeroPool(sizeof(*ppDimms) * MAX_DIMMS); if (ppDimms == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pDimmsSym = AllocateZeroPool(sizeof(*pDimmsSym) * MAX_DIMMS); if (pDimmsSym == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pDimmsAsym = AllocateZeroPool(sizeof(*pDimmsAsym) * MAX_DIMMS); if (pDimmsAsym == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } //DCPMMs in population violation are ignored from all goal requests except in the case that the goal //request is for ADx1 100%. In this case DCPMMs in population violation can be used. if (!((PM_TYPE_AD_NI == PersistentMemType) && (0 == *pVolatilePercent))) { RequireDcpmmsBitfield |= REQUIRE_DCPMMS_NO_POPULATION_VIOLATION; } ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, pSocketIds, SocketIdsCount, RequireDcpmmsBitfield, ppDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode) || pCommandStatus->GeneralStatus != NVM_ERR_OPERATION_NOT_STARTED) { goto Finish; } if (pNumOfDimmsTargeted != NULL) { *pNumOfDimmsTargeted = DimmsNum; } ReturnCode = RetrieveGoalConfigsFromPlatformConfigData(&gNvmDimmData->PMEMDev.Dimms, FALSE); if (EFI_ERROR(ReturnCode)) { if (EFI_NO_RESPONSE == ReturnCode) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); } else if (ReturnCode == EFI_VOLUME_CORRUPTED) { ResetCmdStatus(pCommandStatus, NVM_ERR_PCD_BAD_DEVICE_CONFIG); } goto Finish; } ReturnCode = CheckForExistingGoalConfigPerSocket(ppDimms, &DimmsNum); if (ReturnCode == EFI_ABORTED) { ResetCmdStatus(pCommandStatus, NVM_ERR_REGION_CURR_CONF_EXISTS); NVDIMM_DBG("Current Goal Configuration exists. Operation Aborted"); goto Finish; } else if (EFI_ERROR(ReturnCode)) { goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = GetDimmSecurityState(ppDimms[Index], PT_TIMEOUT_INTERVAL, &DimmSecurityStateMask); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (!IsConfiguringForCreateGoalAllowed(DimmSecurityStateMask)) { ReturnCode = EFI_ACCESS_DENIED; ResetCmdStatus(pCommandStatus, NVM_ERR_CREATE_GOAL_NOT_ALLOWED); NVDIMM_DBG("Invalid request to create goal while security is in locked state."); goto Finish; } if ((DimmSecurityStateMask & SECURITY_MASK_ENABLED) && !(DimmSecurityStateMask & SECURITY_MASK_LOCKED)) { IsDimmUnlocked = TRUE; } } if (IsDimmUnlocked) { SetCmdStatus(pCommandStatus, NVM_WARN_GOAL_CREATION_SECURITY_UNLOCKED); } ReturnCode = PersistentMemoryTypeValidation(PersistentMemType); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (ReserveDimm != RESERVE_DIMM_NONE && ReserveDimm != RESERVE_DIMM_AD_NOT_INTERLEAVED) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /** Mark reserve Dimm **/ if (ReserveDimm != RESERVE_DIMM_NONE) { ReturnCode = SelectReserveDimm(ppDimms, &DimmsNum, &pReserveDimm); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (DimmsNum == 0) { ResetCmdStatus(pCommandStatus, NVM_ERR_RESERVE_DIMM_REQUIRES_AT_LEAST_TWO_DIMMS); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } // allocate helper structures pDimmsSymPerSocket = AllocateZeroPool(sizeof(*pDimmsSym) * MAX_DIMMS); if (pDimmsSymPerSocket == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pDimmsAsymPerSocket = AllocateZeroPool(sizeof(*pDimmsAsym) * MAX_DIMMS); if (pDimmsAsymPerSocket == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) { ReturnCode = RetrieveMaxPMInterleaveSets(&MaxPMInterleaveSets); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (pMaxPMInterleaveSetsPerDie != NULL) { *pMaxPMInterleaveSetsPerDie = MaxPMInterleaveSets.MaxInterleaveSetsSplit.PerDie; } } for (Socket = 0; Socket < MAX_SOCKETS; Socket++) { DimmsAsymNumPerSocket = 0; DimmsSymNumPerSocket = 0; ZeroMem(pDimmsOnSocket, sizeof(pDimmsOnSocket[0]) * MAX_DIMMS); FilterDimmBySocket(Socket, ppDimms, DimmsNum, pDimmsOnSocket, &NumDimmsOnSocket); /**User might have created goal for 2nd socket alone*/ if (NumDimmsOnSocket <= 0) { continue; } /** Calculate volatile percent **/ ReturnCode = CalculateDimmCapacityFromPercent(pDimmsOnSocket, NumDimmsOnSocket, *pVolatilePercent, &ActualVolatileSize); if (EFI_ERROR(ReturnCode)) { goto Finish; } /* calculate the total requested volatile size */ TotalInputVolatileSize += ActualVolatileSize; ReturnCode = MapRequestToActualRegionGoalTemplates(pDimmsOnSocket, NumDimmsOnSocket, pDimmsSymPerSocket, &DimmsSymNumPerSocket, pDimmsAsymPerSocket, &DimmsAsymNumPerSocket, PersistentMemType, ActualVolatileSize, ReservedPercent, ((IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) ? &MaxPMInterleaveSets : NULL), &ActualVolatileSize, RegionGoalTemplates, &RegionGoalTemplatesNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } TotalActualVolatileSize += ActualVolatileSize; ReturnCode = ReduceCapacityForSocketSKU(Socket, pDimmsOnSocket, NumDimmsOnSocket, pDimmsSymPerSocket, &DimmsSymNumPerSocket, pDimmsAsymPerSocket, &DimmsAsymNumPerSocket, RegionGoalTemplates, &RegionGoalTemplatesNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } // Check for NM:FM ratio and set command status if not within limits ReturnCode = CheckNmFmLimits((UINT16)Socket, pDimmsSymPerSocket, DimmsSymNumPerSocket, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (pDimmsSym != NULL && pDimmsSymPerSocket != NULL && (DimmsSymNum < MAX_DIMMS)) { for (Index = 0; Index < DimmsSymNumPerSocket; Index++) { pDimmsSym[DimmsSymNum] = pDimmsSymPerSocket[Index]; DimmsSymNum++; } } if (pDimmsAsym != NULL && pDimmsAsymPerSocket != NULL && (DimmsAsymNum < MAX_DIMMS)) { for (Index = 0; Index < DimmsAsymNumPerSocket; Index++) { pDimmsAsym[DimmsAsymNum] = pDimmsAsymPerSocket[Index]; DimmsAsymNum++; } } } // We have removed all Asymmetrical memory from the system region templates should be reduced if (DimmsAsymNum == 0) { RegionGoalTemplatesNum = 1; } // We have removed all symmetrical memory from the system and region templates should be reduced if (DimmsSymNum == 0) { RegionGoalTemplatesNum = 0; } /** Calculate actual volatile percent **/ if (TotalInputVolatileSize != 0) { *pVolatilePercent = (UINT32) ((*pVolatilePercent) * TotalActualVolatileSize / TotalInputVolatileSize); } else { *pVolatilePercent = 0; } // Fill in the ConfigGoals with enough information to be used by show goal for (Index = 0; Index < DimmsSymNum; Index++) { pConfigGoals[*pConfigGoalsCount].SocketId = pDimmsSym[Index].pDimm->SocketId; pConfigGoals[*pConfigGoalsCount].DimmID = pDimmsSym[Index].pDimm->DeviceHandle.AsUint32; ReturnCode = GetDimmUid(pDimmsSym[Index].pDimm, pConfigGoals[*pConfigGoalsCount].DimmUid, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } pConfigGoals[*pConfigGoalsCount].VolatileSize = pDimmsSym[Index].VolatileSize; pConfigGoals[*pConfigGoalsCount].AppDirectSize[0] = pDimmsSym[Index].RegionSize; (*pConfigGoalsCount)++; } for (Index = 0; Index < DimmsAsymNum; Index++) { Found = FALSE; for (Index2 = 0; Index < *pConfigGoalsCount; Index2++) { if (pDimmsAsym[Index].pDimm->DeviceHandle.AsUint32 == pConfigGoals[Index2].DimmID) { pConfigGoals[Index2].AppDirectSize[1] = pDimmsAsym[Index].RegionSize; Found = TRUE; break; } } if (!Found) { pConfigGoals[*pConfigGoalsCount].DimmID = pDimmsAsym[Index].pDimm->DeviceHandle.AsUint32; ReturnCode = GetDimmUid(pDimmsAsym[Index].pDimm, pConfigGoals[*pConfigGoalsCount].DimmUid, MAX_DIMM_UID_LENGTH); if (EFI_ERROR(ReturnCode)) { goto Finish; } pConfigGoals[*pConfigGoalsCount].AppDirectSize[0] = pDimmsAsym[Index].RegionSize; (*pConfigGoalsCount)++; } } ReturnCode = AllowedMemoryMode(&AllowedMode); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to determine system memory mode"); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } if (!IS_BIOS_VOLATILE_MEMORY_MODE_2LM(AllowedMode)) { /** Check if volatile memory has been requested **/ for (Index = 0; Index < *pConfigGoalsCount; Index++) { if (pConfigGoals[Index].VolatileSize > 0) { ReturnCode = EFI_UNSUPPORTED; ResetCmdStatus(pCommandStatus, NVM_ERR_PLATFORM_NOT_SUPPORT_2LM_MODE); break; } } } // Only overwrite the general status if we had nothing else to say if (pCommandStatus->GeneralStatus == NVM_ERR_OPERATION_NOT_STARTED) { SetCmdStatus(pCommandStatus, NVM_SUCCESS); } Finish: FREE_POOL_SAFE(ppDimms); FREE_POOL_SAFE(pDimmsSym); FREE_POOL_SAFE(pDimmsAsym); FREE_POOL_SAFE(pDimmsSymPerSocket); FREE_POOL_SAFE(pDimmsAsymPerSocket); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Create region goal configuration @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Examine Do a dry run if set @param[in] pDimmIds Pointer to an array of DIMM IDs @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[in] PersistentMemType Persistent memory type @param[in] VolatilePercent Volatile region size in percents @param[in] ReservedPercent Amount of AppDirect memory to not map in percents @param[in] ReserveDimm Reserve one DIMM for use as a not interleaved AppDirect memory @param[in] LabelVersionMajor Major version of label to init @param[in] LabelVersionMinor Minor version of label to init @param[out] pMaxPMInterleaveSetsPerDie pointer to Maximum PM Interleave Sets per Die @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_NO_RESPONSE FW busy for one or more dimms @retval EFI_SUCCESS All Ok **/ EFI_STATUS EFIAPI CreateGoalConfig( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN BOOLEAN Examine, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds OPTIONAL, IN UINT32 SocketIdsCount, IN UINT8 PersistentMemType, IN UINT32 VolatilePercent, IN UINT32 ReservedPercent, IN UINT8 ReserveDimm, IN UINT16 LabelVersionMajor, IN UINT16 LabelVersionMinor, OUT UINT32 *pMaxPMInterleaveSetsPerDie OPTIONAL, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM **ppDimms = NULL; UINT32 DimmsNum = 0; REGION_GOAL_DIMM *pDimmsSymPerSocket = NULL; UINT32 DimmsSymNumPerSocket = 0; REGION_GOAL_DIMM *pDimmsAsymPerSocket = NULL; UINT32 DimmsAsymNumPerSocket = 0; DIMM *pReserveDimm = NULL; UINT32 DimmSecurityStateMask = 0; UINT64 VolatileSize = 0; UINT64 ReservedSize = 0; BOOLEAN Found = FALSE; DRIVER_PREFERENCES DriverPreferences; BOOLEAN Conflict = FALSE; REGION_GOAL_TEMPLATE RegionGoalTemplates[MAX_IS_PER_DIMM]; UINT32 RegionGoalTemplatesNum = 0; UINT32 Index = 0; UINT32 Socket = 0; DIMM *pDimmsOnSocket[MAX_DIMMS]; UINT32 NumDimmsOnSocket = 0; REGION_GOAL_DIMM *pDimmsSym = NULL; UINT32 DimmsSymNum = 0; REGION_GOAL_DIMM *pDimmsAsym = NULL; UINT32 DimmsAsymNum = 0; MAX_PMINTERLEAVE_SETS MaxPMInterleaveSets; ACPI_REVISION PcatRevision; BOOLEAN SendGoalConfigWarning = FALSE; BOOLEAN OnlyUnrelatedMailboxesOnList = FALSE; BOOLEAN SmbusMailboxOnList = FALSE; BOOLEAN SmbusWillNotBeUsed = FALSE; DIMM_PASSTHRU_METHOD Method = DimmPassthruDdrtLargePayload; REQUIRE_DCPMMS RequireDcpmmsBitfield = REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_FUNCTIONAL | REQUIRE_DCPMMS_MEDIA_ACCESSIBLE; NVDIMM_ENTRY(); ZeroMem(RegionGoalTemplates, sizeof(RegionGoalTemplates)); ZeroMem(&DriverPreferences, sizeof(DriverPreferences)); ZeroMem(&MaxPMInterleaveSets, sizeof(MaxPMInterleaveSets)); ZeroMem(&PcatRevision, sizeof(PcatRevision)); if (pThis == NULL || pCommandStatus == NULL || VolatilePercent > 100 || ReservedPercent > 100 || VolatilePercent + ReservedPercent > 100) { ReturnCode = EFI_INVALID_PARAMETER; ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_PARAMETER); NVDIMM_DBG("Invalid Parameter"); goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead != NULL) { PcatRevision.AsUint8 = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr->Header.Revision.AsUint8; } CHECK_RESULT(BlockMixedSku(pCommandStatus), Finish); ppDimms = AllocateZeroPool(sizeof(*ppDimms) * MAX_DIMMS); if (ppDimms == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pDimmsSym = AllocateZeroPool(sizeof(*pDimmsSym) * MAX_DIMMS); if (pDimmsSym == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pDimmsAsym = AllocateZeroPool(sizeof(*pDimmsAsym) * MAX_DIMMS); if (pDimmsAsym == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } //DCPMMs in population violation are ignored from all goal requests except in the case that the goal //request is for ADx1 100%. In this case DCPMMs in population violation can be used. if (!((PM_TYPE_AD_NI == PersistentMemType) && (0 == VolatilePercent))) { RequireDcpmmsBitfield |= REQUIRE_DCPMMS_NO_POPULATION_VIOLATION; } ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, pSocketIds, SocketIdsCount, RequireDcpmmsBitfield, ppDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode) || pCommandStatus->GeneralStatus != NVM_ERR_OPERATION_NOT_STARTED) { goto Finish; } /** Verify Command Access Policy for Set PCD command **/ UINT8 CapRestricted = 0; EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS Attribs; CHECK_RESULT(GetFisTransportAttributes(pThis, &Attribs), Finish); for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = FwCmdGetCommandAccessPolicy(ppDimms[Index], PtSetAdminFeatures, SubopPlatformDataInfo, &CapRestricted); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to retrieve Command Access Policy for %d:%d. DimmID=0x%04x ReturnCode=%d.", PtSetAdminFeatures, SubopPlatformDataInfo, ppDimms[Index]->DimmID, ReturnCode); goto Finish; } // We're doing this pre-check (can we run Set PCD?) so the user // doesn't have to go through the process of creating and confirming a // goal only to have it fail to apply. We can only run Set PCD if there is // no restriction (OS mailbox using DDRT) or if we use SMBus and either there // is no restriction or SMBus is in the restriction list (it's allowed). // (Using SMBus for create -goal is nonsensical in practice, but we'll leave // it in) In all other cases, we want to pre-emptively say that create -goal // is unsupported, which is the logic below. // First, we can't do anything if we're restricted to BIOS and/or management // (BMC/ME) mailbox only. OnlyUnrelatedMailboxesOnList = (CapRestricted == COMMAND_ACCESS_POLICY_RESTRICTION_BIOSONLY || CapRestricted == COMMAND_ACCESS_POLICY_RESTRICTION_MGMTONLY || CapRestricted == COMMAND_ACCESS_POLICY_RESTRICTION_MGMTBIOSONLY); // If the restriction list includes SMBus but SMBus will not be used, // then we can't do anything. SmbusMailboxOnList = (CapRestricted == COMMAND_ACCESS_POLICY_RESTRICTION_SMBUSONLY || CapRestricted == COMMAND_ACCESS_POLICY_RESTRICTION_BIOSSMBUSONLY || CapRestricted == COMMAND_ACCESS_POLICY_RESTRICTION_MGMTSMBUSONLY || CapRestricted == COMMAND_ACCESS_POLICY_RESTRICTION_MGMTBIOSSMBUSONLY); CHECK_RESULT(DeterminePassThruMethod(ppDimms[Index], PtSetAdminFeatures, SubopPlatformDataInfo, TRUE, &Method), Finish); SmbusWillNotBeUsed = (Method != DimmPassthruSmbusSmallPayload); // The final logic should be readable now if (OnlyUnrelatedMailboxesOnList || (SmbusMailboxOnList && SmbusWillNotBeUsed)) { ReturnCode = EFI_UNSUPPORTED; NVDIMM_WARN("Command access policy disallows Set PCD command"); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_NOT_SUPPORTED); goto Finish; } } #ifdef OS_BUILD // TODO: Optimize the number of LSA reads happening when interleave sets // are initialized using NFIT & PCD both and namespaces are initialized. // Trigger Interleave Set initialization from PCD InitializeInterleaveSets(FALSE); // Trigger Interleave Set initialization from NFIT InitializeInterleaveSets(TRUE); ReturnCode = InitializeNamespaces(); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to initialize Namespaces, error = " FORMAT_EFI_STATUS ".", ReturnCode); } #endif ReturnCode = IsNamespaceOnDimms(ppDimms, DimmsNum, &Found); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (Found) { ReturnCode = EFI_ABORTED; ResetCmdStatus(pCommandStatus, NVM_ERR_REGION_GOAL_NAMESPACE_EXISTS); NVDIMM_DBG("Operation Aborted. Namespaces exists on current Region, need to be deleted before creating Goal"); goto Finish; } if (ReserveDimm != RESERVE_DIMM_NONE && ReserveDimm != RESERVE_DIMM_AD_NOT_INTERLEAVED) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (LabelVersionMajor != NSINDEX_MAJOR) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if ((LabelVersionMinor != NSINDEX_MINOR_1) && (LabelVersionMinor != NSINDEX_MINOR_2)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = RetrieveGoalConfigsFromPlatformConfigData(&gNvmDimmData->PMEMDev.Dimms, TRUE); if (EFI_ERROR(ReturnCode)) { if (EFI_NO_RESPONSE == ReturnCode) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); } goto Finish; } ReturnCode = CheckForExistingGoalConfigPerSocket(ppDimms, &DimmsNum); if (ReturnCode == EFI_ABORTED) { ResetCmdStatus(pCommandStatus, NVM_ERR_REGION_CURR_CONF_EXISTS); NVDIMM_DBG("Current Goal Configuration exists. Operation Aborted"); goto Finish; } else if (EFI_ERROR(ReturnCode)) { goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = GetDimmSecurityState(ppDimms[Index], PT_TIMEOUT_INTERVAL, &DimmSecurityStateMask); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (!IsConfiguringForCreateGoalAllowed(DimmSecurityStateMask)) { ReturnCode = EFI_ACCESS_DENIED; ResetCmdStatus(pCommandStatus, NVM_ERR_CREATE_GOAL_NOT_ALLOWED); NVDIMM_DBG("Invalid request to create goal while security is in locked state."); goto Finish; } if (IS_DIMM_SECURITY_ENABLED(DimmSecurityStateMask)) { SendGoalConfigWarning = TRUE; } } ReturnCode = PersistentMemoryTypeValidation(PersistentMemType); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** User has to configure all the unconfigured DIMMs or all DIMMs on a given socket at once **/ ReturnCode = VerifyCreatingSupportedRegionConfigs(ppDimms, DimmsNum, PersistentMemType, VolatilePercent, pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("VerifyCreatingSupportedRegionConfigs Error"); goto Finish; } /** Mark reserve Dimm **/ if (ReserveDimm != RESERVE_DIMM_NONE) { ReturnCode = SelectReserveDimm(ppDimms, &DimmsNum, &pReserveDimm); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (DimmsNum == 0) { ResetCmdStatus(pCommandStatus, NVM_ERR_RESERVE_DIMM_REQUIRES_AT_LEAST_TWO_DIMMS); ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("Marking Reserve Dimm requires at least two dimms"); goto Finish; } } /** Calculate total volatile size after including all Dimms in the request**/ ReturnCode = CalculateDimmCapacityFromPercent(ppDimms, DimmsNum, VolatilePercent, &VolatileSize); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Calculate total Reserved size after including all the Dimms in the request**/ ReturnCode = CalculateDimmCapacityFromPercent(ppDimms, DimmsNum, ReservedPercent, &ReservedSize); if (EFI_ERROR(ReturnCode)) { goto Finish; } // TODO: need to refactor this more. // PM_TYPE_RESERVED is used to NOT calculate AD capacity /** If Volatile and Reserved Percent sum to 100 then never map Appdirect even if alignment would allow it **/ if (VolatilePercent + ReservedPercent == 100) { PersistentMemType = PM_TYPE_RESERVED; } /** Check platform support **/ ReturnCode = VerifyPlatformSupport(VolatileSize, PersistentMemType, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = ReadRunTimeDriverPreferences(&DriverPreferences); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Check interleaving settings are correct **/ ReturnCode = AppDirectSettingsValidation(&DriverPreferences); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("AppDirectSettingValidation Error"); goto Finish; } /** Check that the AppDirect Memory in the system does not conflict with preferences **/ ReturnCode = AppDirectSettingsConflict(&DriverPreferences, &Conflict, pCommandStatus); if (EFI_ERROR(ReturnCode) || Conflict) { NVDIMM_ERR("AppDirectSettingConflict Errors with CommandStatus set to NVM_ERR_APPDIRECT_IN_SYSTEM"); goto Finish; } // allocate helper structures pDimmsSymPerSocket = AllocateZeroPool(sizeof(*pDimmsSym) * MAX_DIMMS); if (pDimmsSymPerSocket == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pDimmsAsymPerSocket = AllocateZeroPool(sizeof(*pDimmsAsym) * MAX_DIMMS); if (pDimmsAsymPerSocket == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) { ReturnCode = RetrieveMaxPMInterleaveSets(&MaxPMInterleaveSets); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (pMaxPMInterleaveSetsPerDie != NULL) { *pMaxPMInterleaveSetsPerDie = MaxPMInterleaveSets.MaxInterleaveSetsSplit.PerDie; } } /** Calculate volatile and AD capacities at the socket level **/ for (Socket = 0; Socket < MAX_SOCKETS; Socket++) { DimmsAsymNumPerSocket = 0; DimmsSymNumPerSocket = 0; ZeroMem(pDimmsOnSocket, sizeof(pDimmsOnSocket[0]) * MAX_DIMMS); FilterDimmBySocket(Socket, ppDimms, DimmsNum, pDimmsOnSocket, &NumDimmsOnSocket); if (NumDimmsOnSocket <= 0) { continue; } /** Calculate volatile percent **/ ReturnCode = CalculateDimmCapacityFromPercent(pDimmsOnSocket, NumDimmsOnSocket, VolatilePercent, &VolatileSize); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = MapRequestToActualRegionGoalTemplates(pDimmsOnSocket, NumDimmsOnSocket, pDimmsSymPerSocket, &DimmsSymNumPerSocket, pDimmsAsymPerSocket, &DimmsAsymNumPerSocket, PersistentMemType, VolatileSize , ReservedPercent, ((IS_ACPI_REV_MAJ_1_OR_MAJ_3(PcatRevision)) ? &MaxPMInterleaveSets : NULL), NULL, RegionGoalTemplates, &RegionGoalTemplatesNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = ReduceCapacityForSocketSKU(Socket, pDimmsOnSocket, NumDimmsOnSocket, pDimmsSymPerSocket, &DimmsSymNumPerSocket, pDimmsAsymPerSocket, &DimmsAsymNumPerSocket, RegionGoalTemplates, &RegionGoalTemplatesNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("ReduceCapacityForSocketSKU Error"); goto Finish; } // Check for NM:FM ratio and set command status if not within limits ReturnCode = CheckNmFmLimits((UINT16)Socket, pDimmsSymPerSocket, DimmsSymNumPerSocket, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } /**Add the symmetrical and Asymmetrical size to the global list **/ if (pDimmsSym != NULL && pDimmsSymPerSocket != NULL && (DimmsSymNum < MAX_DIMMS)) { for (Index = 0; Index < DimmsSymNumPerSocket; Index++) { pDimmsSym[DimmsSymNum] = pDimmsSymPerSocket[Index]; DimmsSymNum++; } } if (pDimmsAsym != NULL && pDimmsAsymPerSocket != NULL && (DimmsAsymNum < MAX_DIMMS)) { for (Index = 0; Index < DimmsAsymNumPerSocket; Index++) { pDimmsAsym[DimmsAsymNum] = pDimmsAsymPerSocket[Index]; DimmsAsymNum++; } } } // We have removed all Asymmetrical memory, so decrease the number of goal templates if (DimmsAsymNum == 0) { RegionGoalTemplatesNum = 1; } // We have removed all symmetrical memory from the system, make the goal template num 0 if (DimmsSymNum == 0) { RegionGoalTemplatesNum = 0; } /** Update internal driver's structures **/ ReturnCode = MapRegionsGoal(pDimmsSym, DimmsSymNum, pDimmsAsym, DimmsAsymNum, pReserveDimm, ReserveDimm, VolatileSize, RegionGoalTemplates, RegionGoalTemplatesNum, &DriverPreferences, pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("MapRegionsGoal Error"); goto Finish; } ReturnCode = VerifySKUSupportForCreateGoal(ppDimms, DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("VerifySKUSupportForCreateGoal Error"); goto Finish; } if (Examine) { SetCmdStatus(pCommandStatus, NVM_SUCCESS); } else { /** Send Platform Config Data to DIMMs **/ ReturnCode = ApplyGoalConfigsToDimms(ppDimms, DimmsNum, (ReservedSize == 0), pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("ApplyGoalConfigsToDimms Error"); goto Finish; } /** Initialize namespace label storage area **/ ReturnCode = ClearAndInitializeAllLabelStorageAreas(ppDimms, DimmsNum, LabelVersionMajor, LabelVersionMinor, pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("ClearAndInitializeAllLabelStorageAreas Error"); goto Finish; } /* Set pCommandStatus to warning if security state unlocked */ if (SendGoalConfigWarning) { SetCmdStatus(pCommandStatus, NVM_WARN_GOAL_CREATION_SECURITY_UNLOCKED); } } Finish: ClearInternalGoalConfigsInfo(&gNvmDimmData->PMEMDev.Dimms); ClearPcdCacheOnDimmList(); FREE_POOL_SAFE(ppDimms); FREE_POOL_SAFE(pDimmsSym); FREE_POOL_SAFE(pDimmsAsym); NVDIMM_EXIT_I64(ReturnCode); FREE_POOL_SAFE(pDimmsSymPerSocket); FREE_POOL_SAFE(pDimmsAsymPerSocket); return ReturnCode; } /** Delete region goal configuration @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of DIMM IDs @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_NO_RESPONSE FW busy for one or more dimms @retval EFI_SUCCESS All Ok **/ EFI_STATUS EFIAPI DeleteGoalConfig ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds OPTIONAL, IN UINT32 SocketIdsCount, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; UINT32 DimmSecurityState = 0; UINT32 Index = 0; SetMem(pDimms, sizeof(pDimms), 0x0); NVDIMM_ENTRY(); if (pThis == NULL || pCommandStatus == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } CHECK_RESULT(BlockMixedSku(pCommandStatus), Finish); /** Verify input parameters and determine a list of DIMMs **/ ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, pSocketIds, SocketIdsCount, REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_MEDIA_ACCESSIBLE, pDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode) || pCommandStatus->GeneralStatus != NVM_ERR_OPERATION_NOT_STARTED) { goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = GetDimmSecurityState(pDimms[Index], PT_TIMEOUT_INTERVAL, &DimmSecurityState); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (!IsConfiguringForCreateGoalAllowed(DimmSecurityState)) { ReturnCode = EFI_ACCESS_DENIED; ResetCmdStatus(pCommandStatus, NVM_ERR_CREATE_GOAL_NOT_ALLOWED); NVDIMM_DBG("Invalid request to create goal while security is in locked state."); goto Finish; } } ReturnCode = RetrieveGoalConfigsFromPlatformConfigData(&gNvmDimmData->PMEMDev.Dimms, FALSE); if (EFI_VOLUME_CORRUPTED == ReturnCode) { ResetCmdStatus(pCommandStatus, NVM_ERR_PCD_BAD_DEVICE_CONFIG); goto Finish; } else if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); goto Finish; } /** User has to deconfigure all dimms with goal config on a given socket at once to keep supported region configs **/ ReturnCode = VerifyDeletingSupportedRegionConfigs(pDimms, DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Update internal driver's structures **/ ReturnCode = DeleteRegionsGoalConfigs(pDimms, DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } /** Send Platform Config Data to DIMMs **/ // ReservedSizeIsZero (the TRUE) isn't really relevant here (we are deleting goals) // but using TRUE for backwards compatible behavior internally just in case ReturnCode = ApplyGoalConfigsToDimms(pDimms, DimmsNum, TRUE, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } Finish: ClearInternalGoalConfigsInfo(&gNvmDimmData->PMEMDev.Dimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Dump region goal configuration into the file @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pFilePath Name is a pointer to a dump file path @param[in] pDevicePath is a pointer to a device where dump file will be stored @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_NO_RESPONSE FW busy for one or more dimms @retval EFI_SUCCESS All Ok **/ EFI_STATUS EFIAPI DumpGoalConfig( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN CHAR16 *pFilePath, IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; //#ifndef OS_BUILD EFI_FILE_HANDLE pFileHandle = NULL; DIMM_CONFIG *pDimmConfigs = NULL; UINT32 DimmConfigsNum = 0; #ifndef OS_BUILD UINT64 FileSize = 0; #endif NVDIMM_ENTRY(); if (pThis == NULL || pFilePath == NULL || pCommandStatus == NULL) { goto Finish; } #ifndef OS_BUILD if (pDevicePath == NULL) { goto Finish; } #endif ReturnCode = ReenumerateNamespacesAndISs(FALSE); if (EFI_ERROR(ReturnCode)) { if (EFI_NO_RESPONSE == ReturnCode) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); } goto Finish; } #ifdef OS_BUILD //triggers PCD read InitializeInterleaveSets(FALSE); #endif /** Get an array of dimms' current config **/ ReturnCode = GetDimmsCurrentConfig(&pDimmConfigs, &DimmConfigsNum); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (DimmConfigsNum == 0) { ResetCmdStatus(pCommandStatus, NVM_ERR_DUMP_NO_CONFIGURED_DIMMS); ReturnCode = EFI_ABORTED; goto Finish; } #ifndef OS_BUILD /** Create new file for dump **/ ReturnCode = OpenFileByDevice(pFilePath, pDevicePath, TRUE, &pFileHandle); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed on create file to dump Region goal configuration. (" FORMAT_EFI_STATUS ")", ReturnCode); ResetCmdStatus(pCommandStatus, NVM_ERR_OPEN_FILE_WITH_WRITE_MODE_FAILED); goto Finish; } /** Get File Size **/ ReturnCode = GetFileSize(pFileHandle, &FileSize); /** If file already exists and has some size, delete it If file already exists and is empty, use it **/ if (FileSize != 0) { ReturnCode = pFileHandle->Delete(pFileHandle); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed on deleting old dump file! (" FORMAT_EFI_STATUS ")", ReturnCode); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } /** Create new file for dump **/ ReturnCode = OpenFileByDevice(pFilePath, pDevicePath, TRUE, &pFileHandle); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed on create file to dump Region Goal Configuration. (" FORMAT_EFI_STATUS ")", ReturnCode); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } } #else ReturnCode = OpenFileText(pFilePath, &pFileHandle, NULL, 1); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed on open dump file header info. (" FORMAT_EFI_STATUS ")", ReturnCode); ResetCmdStatus(pCommandStatus, NVM_ERR_DUMP_FILE_OPERATION_FAILED); goto Finish; } #endif /** Write dump file header **/ ReturnCode = WriteDumpFileHeader(pFileHandle); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed on write dump file header info. (" FORMAT_EFI_STATUS ")", ReturnCode); ResetCmdStatus(pCommandStatus, NVM_ERR_DUMP_FILE_OPERATION_FAILED); goto Finish; } /** Perform Dump to File **/ ReturnCode = DumpConfigToFile(pFileHandle, pDimmConfigs, DimmConfigsNum); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed on write dump. (" FORMAT_EFI_STATUS ")", ReturnCode); ResetCmdStatus(pCommandStatus, NVM_ERR_DUMP_FILE_OPERATION_FAILED); goto Finish; } ResetCmdStatus(pCommandStatus, NVM_SUCCESS); ReturnCode = EFI_SUCCESS; Finish: if (pFileHandle != NULL) { pFileHandle->Close(pFileHandle); } FREE_POOL_SAFE(pDimmConfigs); NVDIMM_EXIT_I64(ReturnCode); // #endif return ReturnCode; } /** Load region goal configuration from file @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of DIMM IDs @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[in] pFileString Buffer for Region Goal configuration from file @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All Ok **/ EFI_STATUS EFIAPI LoadGoalConfig( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds, IN UINT32 SocketIdsCount, IN CHAR8 *pFileString, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; UINT32 Socket = 0; DIMM_CONFIG *pDimmsConfig = NULL; UINT16 DimmIDs[MAX_DIMMS_PER_SOCKET]; UINT32 DimmIDsNum = 0; COMMAND_STATUS *pCmdStatusInternal = NULL; UINT32 DimmSecurityState = 0; UINT8 PersistentMemType = 0; UINT32 VolatilePercent = 0; UINT32 ReservedPercent = 0; UINT16 LabelVersionMajor = 0; UINT16 LabelVersionMinor = 0; UINT32 Index = 0; ZeroMem(pDimms, sizeof(pDimms)); ZeroMem(DimmIDs, sizeof(DimmIDs)); NVDIMM_ENTRY(); if (pThis == NULL || pCommandStatus == NULL || pFileString == NULL || (pDimmIds == NULL && DimmIdsCount > 0) || (pSocketIds == NULL && SocketIdsCount > 0)) { goto Finish; } CHECK_RESULT(BlockMixedSku(pCommandStatus), Finish); ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, pSocketIds, SocketIdsCount, REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_FUNCTIONAL | REQUIRE_DCPMMS_MEDIA_ACCESSIBLE, pDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode) || pCommandStatus->GeneralStatus != NVM_ERR_OPERATION_NOT_STARTED) { goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = GetDimmSecurityState(pDimms[Index], PT_TIMEOUT_INTERVAL, &DimmSecurityState); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (!IsConfiguringForCreateGoalAllowed(DimmSecurityState)) { ReturnCode = EFI_ACCESS_DENIED; ResetCmdStatus(pCommandStatus, NVM_ERR_CREATE_GOAL_NOT_ALLOWED); NVDIMM_DBG("Invalid request to create goal while security is in locked state."); goto Finish; } } // Allocate memory for load Region Goal Configuration pDimmsConfig = AllocateZeroPool(sizeof(*pDimmsConfig) * DimmsNum); if (pDimmsConfig == NULL) { NVDIMM_DBG("Failed to allocate memory for Dimm Config array."); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } // Parse source file ReturnCode = SetUpGoalStructures(pDimms, pDimmsConfig, DimmsNum, pFileString, pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("SetUpGoalStructures failed. (" FORMAT_EFI_STATUS ")",ReturnCode); goto Finish; } for (Socket = 0; Socket < MAX_SOCKETS; Socket++) { pCmdStatusInternal = NULL; ReturnCode = InitializeCommandStatus(&pCmdStatusInternal); if (EFI_ERROR(ReturnCode)) { FreeCommandStatus(&pCmdStatusInternal); goto Finish; } ReturnCode = ValidateAndPrepareLoadConfig((UINT16)Socket, pDimmsConfig, DimmsNum, DimmIDs, &DimmIDsNum, &PersistentMemType, &VolatilePercent, &ReservedPercent, &LabelVersionMajor, &LabelVersionMinor, pCmdStatusInternal); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("ValidAndPrepareLoadConfig failed. (" FORMAT_EFI_STATUS ")", ReturnCode); SetObjStatus(pCommandStatus, Socket, NULL, 0, pCmdStatusInternal->GeneralStatus, ObjectTypeSocket); FreeCommandStatus(&pCmdStatusInternal); goto Finish; } FreeCommandStatus(&pCmdStatusInternal); /** No dimm specified for this socket **/ if (DimmIDsNum == 0) { continue; } pCmdStatusInternal = NULL; ReturnCode = InitializeCommandStatus(&pCmdStatusInternal); if (EFI_ERROR(ReturnCode)) { FreeCommandStatus(&pCmdStatusInternal); goto Finish; } // sanity check if (DimmIDsNum > MAX_DIMMS_PER_SOCKET) { FreeCommandStatus(&pCmdStatusInternal); goto Finish; } ReturnCode = CreateGoalConfig(pThis, FALSE, DimmIDs, DimmIDsNum, NULL, 0, PersistentMemType, VolatilePercent, ReservedPercent, RESERVE_DIMM_NONE, LabelVersionMajor, LabelVersionMinor, NULL, pCmdStatusInternal); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CreateGoalConfig failed. (" FORMAT_EFI_STATUS ")", ReturnCode); SetObjStatus(pCommandStatus, Socket, NULL, 0, pCmdStatusInternal->GeneralStatus, ObjectTypeSocket); FreeCommandStatus(&pCmdStatusInternal); goto Finish; } if (NVM_WARN_GOAL_CREATION_SECURITY_UNLOCKED == pCmdStatusInternal->GeneralStatus) { SetObjStatus(pCommandStatus, Socket, NULL, 0, pCmdStatusInternal->GeneralStatus, ObjectTypeSocket); } FreeCommandStatus(&pCmdStatusInternal); if (NVM_WARN_GOAL_CREATION_SECURITY_UNLOCKED != pCommandStatus->GeneralStatus) { SetObjStatus(pCommandStatus, Socket, NULL, 0, NVM_SUCCESS, ObjectTypeSocket); } } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pDimmsConfig); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Start Diagnostic @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of DIMM IDs @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[in] DiagnosticTests bitfield with selected diagnostic tests to be started @param[in] DimmIdPreference Preference for the Dimm ID (handle or UID) @param[out] ppResult Pointer to the structure with information about test @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_NOT_STARTED Test was not executed @retval EFI_SUCCESS All Ok **/ EFI_STATUS EFIAPI StartDiagnostic( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN CONST UINT8 DiagnosticTests, IN UINT8 DimmIdPreference, OUT DIAG_INFO **ppResultStr ) { DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; LIST_ENTRY *pCurrentDimmNode = NULL; LIST_ENTRY *pDimmList = NULL; UINT32 PlatformDimmsCount = 0; DIMM *pCurrentDimm = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; ZeroMem(pDimms, sizeof(pDimms)); NVDIMM_ENTRY(); if (pThis == NULL || ppResultStr == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } //Validating user-passed Dimm IDs, include all dimms if no IDs are passed pDimmList = &gNvmDimmData->PMEMDev.Dimms; ReturnCode = GetListSize(pDimmList, &PlatformDimmsCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on DimmListSize"); goto Finish; } LIST_FOR_EACH(pCurrentDimmNode, pDimmList) { pCurrentDimm = DIMM_FROM_NODE(pCurrentDimmNode); if (pCurrentDimm == NULL) { NVDIMM_DBG("Failed on Get Dimm from node %d", DimmsNum); goto Finish; } pDimms[DimmsNum] = pCurrentDimm; DimmsNum++; } ReturnCode = CoreStartDiagnostics(pDimms, DimmsNum, pDimmIds, DimmIdsCount, DiagnosticTests, DimmIdPreference, ppResultStr); if (EFI_ERROR(ReturnCode)) { goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get Driver API Version @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pVersion output version @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS EFIAPI GetDriverApiVersion( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT CHAR16 pVersion[FW_API_VERSION_LEN] ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pVersion == NULL) { goto Finish; } #ifdef OS_BUILD GetVendorDriverVersion(pVersion, FW_API_VERSION_LEN); ReturnCode = EFI_SUCCESS; #else ConvertFwApiVersion(pVersion, NVMDIMM_MAJOR_API_VERSION, NVMDIMM_MINOR_API_VERSION); ReturnCode = EFI_SUCCESS; #endif Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Create namespace Creates a AppDirect namespace on the provided region/dimm. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] RegionId the ID of the region that the Namespace is supposed to be created. @param[in] Reserved @param[in] BlockSize the size of each of the block in the device. Valid block sizes are: 1 (for AppDirect Namespace), 512 (default), 514, 520, 528, 4096, 4112, 4160, 4224. @param[in] BlockCount the amount of block that this namespace should consist @param[in] pName - Namespace name. If NULL, name will be empty. @param[in] Mode - boolean value to decide when the namespace should have the BTT arena included * 0 - Ignore * 1 - Yes * 2 - No @param[in] ForceAll Suppress all warnings @param[in] ForceAlignment Suppress alignment warnings @param[out] pActualNamespaceCapacity capacity needed to meet alignment requirements @param[out] pNamespaceId Pointer to the ID of the namespace that is created @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS if the operation was successful. @retval EFI_ALREADY_EXISTS if a namespace with the provided GUID already exists in the system. @retval EFI_DEVICE_ERROR if there was a problem with writing the configuration to the device. @retval EFI_OUT_OF_RESOURCES if there is not enough free space on the DIMM/Region. @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system. **/ EFI_STATUS EFIAPI CreateNamespace( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 RegionId, IN UINT16 Reserved, IN UINT32 BlockSize, IN UINT64 BlockCount, IN CHAR8 *pName, IN BOOLEAN Mode, IN BOOLEAN ForceAll, IN BOOLEAN ForceAlignment, OUT UINT64 *pActualNamespaceCapacity, OUT UINT16 *pNamespaceId, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimms[MAX_DIMMS_PER_SOCKET]; NVM_IS *pIS = NULL; LIST_ENTRY *pRegionList = NULL; NAMESPACE *pNamespace = NULL; NAMESPACE_LABEL *pLabel = NULL; NAMESPACE_LABEL **ppLabels = NULL; UINT8 NamespaceType = APPDIRECT_NAMESPACE; UINT16 LabelsCount = 0; GUID NamespaceGUID; UINT64 NamespaceCapacity = 0; UINT64 ActualBlockCount = 0; UINT64 Index = 0; UINT16 Index2 = 0; UINT16 LabelsToRemove = 0; UINT32 Flags = 0; LIST_ENTRY *pNode = NULL; BOOLEAN FailFlag = FALSE; DIMM_REGION *pDimmRegion = NULL; UINT64 ISAvailableCapacity = 0; BOOLEAN CapacitySpecified = FALSE; UINT64 RequestedCapacity = 0; UINT32 DimmCount = 0; UINT64 AlignedNamespaceCapacitySize = 0; UINT64 RegionSize = 0; UINT64 MinSize = 0; UINT64 MaxSize = 0; MEMMAP_RANGE AppDirectRange; BOOLEAN UseLatestLabelVersion = FALSE; NAMESPACE *pNamespace1 = NULL; ZeroMem(pDimms, sizeof(pDimms)); ZeroMem(&NamespaceGUID, sizeof(NamespaceGUID)); ZeroMem(&AppDirectRange, sizeof(AppDirectRange)); REGION_INFO Region; LIST_ENTRY *pDimmList = NULL; LIST_ENTRY *pCurrentDimmNode = NULL; BOOLEAN AreNotPartOfPendingGoal = TRUE; DIMM *pCurrentDimm = NULL; NVDIMM_ENTRY(); SetMem(&Region, sizeof(Region), 0x0); ReturnCode = GetRegionList(&pRegionList, FALSE); if (pThis == NULL || pCommandStatus == NULL || RegionId == REGION_ID_NOTSET || BlockSize == 0 || pActualNamespaceCapacity == NULL || pNamespaceId == NULL || EFI_ERROR(ReturnCode)) { goto Finish; } CHECK_RESULT(BlockMixedSku(pCommandStatus), Finish); *pActualNamespaceCapacity = 0; if (BlockSize != AD_NAMESPACE_BLOCK_SIZE) { ResetCmdStatus(pCommandStatus, NVM_ERR_UNSUPPORTED_BLOCK_SIZE); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } CapacitySpecified = BlockCount != NAMESPACE_BLOCK_COUNT_UNDEFINED; pIS = GetRegionById(pRegionList, RegionId); if (pIS == NULL) { ResetCmdStatus(pCommandStatus, NVM_ERR_REGION_NOT_FOUND); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = ADNamespaceMinAndMaxAvailableSizeOnIS(pIS, &MinSize, &MaxSize); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_NO_RESPONSE) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); } goto Finish; } ReturnCode = GetRegion(pThis, RegionId, &Region, pCommandStatus); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_NO_RESPONSE) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); } goto Finish; } ReturnCode = RetrieveGoalConfigsFromPlatformConfigData(&gNvmDimmData->PMEMDev.Dimms, FALSE); if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); goto Finish; } pDimmList = &gNvmDimmData->PMEMDev.Dimms; // Loop through all dimms associated with specified region until a match is found for (Index = 0; (Index < Region.DimmIdCount) && (AreNotPartOfPendingGoal == TRUE); Index++) { UINT32 NodeCount = 0; LIST_FOR_EACH(pCurrentDimmNode, pDimmList) { pCurrentDimm = DIMM_FROM_NODE(pCurrentDimmNode); if (pCurrentDimm == NULL) { NVDIMM_DBG("Failed on Get Dimm from node %d", NodeCount); ReturnCode = EFI_NOT_FOUND; goto Finish; } NodeCount++; // Valid match found and goal config is pending if ((pCurrentDimm->DeviceHandle.AsUint32 == Region.DimmId[Index]) && (pCurrentDimm->GoalConfigStatus == GOAL_CONFIG_STATUS_NEW)) { AreNotPartOfPendingGoal = FALSE; break; } } } if (!AreNotPartOfPendingGoal) { ResetCmdStatus(pCommandStatus, NVM_ERR_CREATE_NAMESPACE_NOT_ALLOWED); ReturnCode = EFI_ACCESS_DENIED; goto Finish; } if (CapacitySpecified) { /** Calculate namespace capacity to provide **/ RequestedCapacity = BlockCount * BlockSize; if (RequestedCapacity > MaxSize) { ResetCmdStatus(pCommandStatus, NVM_ERR_NOT_ENOUGH_FREE_SPACE); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = ConvertUsableSizeToActualSize(BlockSize, RequestedCapacity, Mode, &ActualBlockCount, pActualNamespaceCapacity, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } } else { // if size is not specified for Namespace, then find the maximum available size NamespaceCapacity = 0; ReturnCode = GetListSize(&pIS->DimmRegionList, &DimmCount); if (EFI_ERROR(ReturnCode) || DimmCount == 0) { goto Finish; } /** Find the free capacity**/ ReturnCode = FindADMemmapRangeInIS(pIS, MAX_UINT64_VALUE, &AppDirectRange); if (EFI_ERROR(ReturnCode) && ReturnCode != EFI_NOT_FOUND) { if (ReturnCode == EFI_NO_RESPONSE) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); } goto Finish; } ReturnCode = EFI_SUCCESS; ISAvailableCapacity = AppDirectRange.RangeLength * DimmCount; if (ISAvailableCapacity > NamespaceCapacity) { NamespaceCapacity = ISAvailableCapacity; } if (NamespaceCapacity == 0) { ReturnCode = EFI_ABORTED; goto Finish; } *pActualNamespaceCapacity = NamespaceCapacity; ActualBlockCount = *pActualNamespaceCapacity / BlockSize; } if (*pActualNamespaceCapacity < MinSize || MinSize == 0) { ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_NAMESPACE_CAPACITY); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /** Namespace capacity that doesn't include block size with 64B cache lane size **/ NamespaceCapacity = ActualBlockCount * BlockSize; ReturnCode = GetListSize(&pIS->DimmRegionList, &DimmCount); if (EFI_ERROR(ReturnCode) || DimmCount == 0) { goto Finish; } ReturnCode = AlignNamespaceCapacity(NamespaceCapacity, DimmCount, &AlignedNamespaceCapacitySize); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Namespace Capacity is an invalid parameter"); ReturnCode = EFI_INVALID_PARAMETER; } RegionSize = AlignedNamespaceCapacitySize / DimmCount; ReturnCode = FindADMemmapRangeInIS(pIS, RegionSize, &AppDirectRange); if (EFI_NOT_FOUND == ReturnCode) { ResetCmdStatus(pCommandStatus, NVM_ERR_NOT_ENOUGH_FREE_SPACE); goto Finish; } else if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG(" AD Mem Map Range not found for pIS"); goto Finish; } /** Build NAMESPACE structure **/ pNamespace = (NAMESPACE *) AllocateZeroPool(sizeof(*pNamespace)); if (pNamespace == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pNamespace->NamespaceId = GenerateNamespaceId(RegionId); pNamespace->Enabled = TRUE; pNamespace->Signature = NAMESPACE_SIGNATURE; pNamespace->Flags.Values.ReadOnly = FALSE; #ifdef WA_ENABLE_LOCAL_FLAG_ON_NS_LABEL_1_2 // Label spec 1.1 LOCAL flag only used for storage mode // Label spec 1.2 LOCAL flag *may* be used for interleave sets on a single device if(!(pNamespace->Major == 1 && pNamespace->Minor < 2)) { if (RegionCount == 1) { pNamespace->Flags.Values.Local = TRUE; } else { pNamespace->Flags.Values.Local = FALSE; } } #endif // WA_ENABLE_LOCAL_FLAG_ON_NS_LABEL_1_2 pNamespace->NamespaceType = NamespaceType; if (Mode) { pNamespace->IsBttEnabled = TRUE; } else { pNamespace->IsRawNamespace = TRUE; } pNamespace->BlockSize = BlockSize; // AppDirect namespaces initially stored with 'updating flag'. pNamespace->Flags.Values.Updating = TRUE; pNamespace->pParentIS = pIS; if (pName != NULL) { CopyMem_S(&pNamespace->Name, sizeof(pNamespace->Name), pName, MIN(AsciiStrLen(pName), NSLABEL_NAME_LEN)); } GenerateNSGUID: GenerateRandomGuid(&NamespaceGUID); LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Namespaces) { pNamespace1 = NAMESPACE_FROM_NODE(pNode, NamespaceNode); if (CompareMem(&pNamespace1->NamespaceGuid, &NamespaceGUID, NSGUID_LEN) == 0) { goto GenerateNSGUID; } } CopyMem_S(&pNamespace->NamespaceGuid, sizeof(pNamespace->NamespaceGuid), &NamespaceGUID, NSGUID_LEN); /** Provision Namespace Capacity using only Region Id **/ ReturnCode = AllocateNamespaceCapacity(NULL, pIS, pActualNamespaceCapacity, pNamespace); pNamespace->BlockCount = *pActualNamespaceCapacity / GetPhysicalBlockSize(pNamespace->BlockSize); pNamespace->UsableSize = *pActualNamespaceCapacity; if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_NOT_ENOUGH_FREE_SPACE); FailFlag = TRUE; goto Finish; } if (pNamespace->RangesCount > MAX_NAMESPACE_RANGES) { FailFlag = TRUE; goto Finish; } if (!ForceAlignment && CapacitySpecified && (RequestedCapacity != *pActualNamespaceCapacity)) { ResetCmdStatus(pCommandStatus, NVM_ERR_BADALIGNMENT); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Find the label version to use ReturnCode = UseLatestNsLabelVersion(pIS, pNamespace->pParentDimm, &UseLatestLabelVersion); if (EFI_ERROR(ReturnCode)) { goto Finish; } ppLabels = AllocateZeroPool(MAX_NAMESPACE_RANGES * sizeof(NAMESPACE_LABEL *)); if (ppLabels == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } for (Index2 = 0; Index2 < pNamespace->RangesCount; Index2++) { pLabel = (NAMESPACE_LABEL *) AllocateZeroPool(sizeof(*pLabel)); if (pLabel == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } if (UseLatestLabelVersion) { pNamespace->Media.BlockSize = AD_NAMESPACE_LABEL_LBA_SIZE_4K; pNamespace->Major = NSINDEX_MAJOR; pNamespace->Minor = NSINDEX_MINOR_2; ReturnCode = CreateNamespaceLabels(pNamespace, Index2, TRUE, pLabel); if (EFI_ERROR(ReturnCode)) { FailFlag = TRUE; goto Finish; } } else { pNamespace->Media.BlockSize = AD_NAMESPACE_LABEL_LBA_SIZE_512; pNamespace->Major = NSINDEX_MAJOR; pNamespace->Minor = NSINDEX_MINOR_1; ReturnCode = CreateNamespaceLabels(pNamespace, Index2, FALSE, pLabel); if (EFI_ERROR(ReturnCode)) { FailFlag = TRUE; goto Finish; } } ppLabels[Index2] = pLabel; LabelsCount++; } /** For BTT Namespaces the structures need to be laid out before updating LSA **/ if (pNamespace->IsBttEnabled) { pNamespace->pBtt = BttInit( GetAccessibleCapacity(pNamespace), (UINT32)GetBlockDeviceBlockSize(pNamespace), (GUID *)pNamespace->NamespaceGuid, pNamespace ); if (pNamespace->pBtt == NULL) { NVDIMM_DBG("Failed to initialize the BTT. Namespace GUID: %g\n", (GUID *)pNamespace->NamespaceGuid); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); FailFlag = TRUE; goto Finish; } // Crating the namespace triggers writing BTT structures ReturnCode = BttWriteLayout(pNamespace->pBtt, TRUE); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to write BTT data"); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); FailFlag = TRUE; goto Finish; } } /** Update NAMESPACE_INDEX with NAMESPACE_LABEL(s) **/ Index2 = 0; LIST_FOR_EACH(pNode, &pIS->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pNode); ReturnCode = InsertNamespaceLabels(pDimmRegion->pDimm, &ppLabels[Index2], 1, pNamespace->Major, pNamespace->Minor); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_SECURITY_VIOLATION) { ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_SECURITY_STATE); } //cleanup already written labels and exit LabelsToRemove = (UINT16)Index2; Index2 = 0; LIST_FOR_EACH(pNode, &pIS->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pNode); if (Index2 >= LabelsToRemove) { break; } RemoveNamespaceLabels(pDimmRegion->pDimm, &ppLabels[Index2]->Uuid, 0); Index2++; } FailFlag = TRUE; goto Finish; } Index2++; } if (Index2 != LabelsCount) { NVDIMM_DBG("Number of labels written is not equal to number of labels."); ResetCmdStatus(pCommandStatus, NVM_ERR_NAMESPACE_CONFIGURATION_BROKEN); FailFlag = TRUE; goto Finish; } /** All ok at this point but one more run is needed to clear updating flag **/ Index2 = 0; LIST_FOR_EACH(pNode, &pIS->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pNode); ppLabels[Index2]->Flags.Values.Updating = FALSE; Flags = ppLabels[Index2]->Flags.AsUint32; ReturnCode = ModifyNamespaceLabels(pDimmRegion->pDimm, &ppLabels[Index2]->Uuid, &Flags, NULL, 0); if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_NAMESPACE_CONFIGURATION_BROKEN); FailFlag = TRUE; goto Finish; } Index2++; } InsertTailList(&pIS->AppDirectNamespaceList, &pNamespace->IsNode); pNamespace->HealthState = NAMESPACE_HEALTH_OK; InsertTailList(&gNvmDimmData->PMEMDev.Namespaces, &pNamespace->NamespaceNode); *pNamespaceId = pNamespace->NamespaceId; ReturnCode = InstallNamespaceProtocols(pNamespace); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_NOT_READY || ReturnCode == EFI_ACCESS_DENIED) { NVDIMM_WARN("Namespace not enabled or invalid DIMM security state! Skipping the protocols installation."); ReturnCode = EFI_SUCCESS; } else if (ReturnCode == EFI_ABORTED) { // The Block Window was not initialized so the platform did not enable the block mode. pNamespace->Enabled = FALSE; NVDIMM_DBG("Block Window equals NULL"); ResetCmdStatus(pCommandStatus, NVM_WARN_BLOCK_MODE_DISABLED); goto Finish; } } if (!EFI_ERROR(ReturnCode)) { SetCmdStatus(pCommandStatus, NVM_SUCCESS); ReturnCode = EFI_SUCCESS; } else { ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); } Finish: for (Index = 0; Index < LabelsCount; Index++) { FREE_POOL_SAFE(ppLabels[Index]); } FREE_POOL_SAFE(ppLabels); if (FailFlag) { if (pNamespace->DimmNode.BackLink != NULL && pNamespace->DimmNode.ForwardLink != NULL) { RemoveEntryList(&pNamespace->DimmNode); } FREE_POOL_SAFE(pNamespace); ReturnCode = EFI_ABORTED; } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get namespaces info @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pNamespaceListNode - pointer to namespace list node @param[out] pNamespacesCount - namespace count @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval EFI_NO_RESPONSE FW busy for one or more dimms @retval EFI_SUCCESS All ok **/ EFI_STATUS EFIAPI GetNamespaces ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN OUT LIST_ENTRY *pNamespaceListNode, OUT UINT32 *pNamespacesCount, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NAMESPACE *pNamespace = NULL; NAMESPACE_INFO *pNamespaceInfo = NULL; LIST_ENTRY *pNode = NULL; if (pThis == NULL || pNamespaceListNode == NULL || pNamespacesCount == NULL || pCommandStatus == NULL) { goto Finish; } ReturnCode = ReenumerateNamespacesAndISs(FALSE); if (EFI_ERROR(ReturnCode)) { if (EFI_NO_RESPONSE == ReturnCode) { ResetCmdStatus(pCommandStatus, NVM_ERR_BUSY_DEVICE); } else if (EFI_VOLUME_CORRUPTED == ReturnCode) { ResetCmdStatus(pCommandStatus, NVM_ERR_PCD_BAD_DEVICE_CONFIG); } #ifdef OS_BUILD goto Finish; #else if ((ReturnCode == EFI_NOT_FOUND && IsLsaNotInitializedOnADimm())) { NVDIMM_WARN("Failure to refresh Namespaces is because LSA not initialized"); } else { goto Finish; } #endif } *pNamespacesCount = 0; LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Namespaces) { pNamespace = NAMESPACE_FROM_NODE(pNode, NamespaceNode); pNamespaceInfo = AllocateZeroPool(sizeof(*pNamespaceInfo)); if (pNamespaceInfo == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } pNamespaceInfo->NamespaceId = pNamespace->NamespaceId; CopyMem_S(pNamespaceInfo->NamespaceGuid, sizeof(pNamespaceInfo->NamespaceGuid), pNamespace->NamespaceGuid, sizeof(pNamespaceInfo->NamespaceGuid)); CopyMem_S(pNamespaceInfo->Name, sizeof(pNamespaceInfo->Name), pNamespace->Name, sizeof(pNamespaceInfo->Name)); pNamespaceInfo->HealthState = pNamespace->HealthState; pNamespaceInfo->BlockSize = pNamespace->BlockSize; pNamespaceInfo->LogicalBlockSize = pNamespace->Media.BlockSize; pNamespaceInfo->BlockCount = pNamespace->BlockCount; pNamespaceInfo->UsableSize = pNamespace->UsableSize; pNamespaceInfo->Major = pNamespace->Major; pNamespaceInfo->Minor = pNamespace->Minor; pNamespaceInfo->NamespaceMode = pNamespace->IsBttEnabled ? SECTOR_MODE : ((pNamespace->IsPfnEnabled) ? FSDAX_MODE : NONE_MODE); pNamespaceInfo->NamespaceType = pNamespace->NamespaceType; pNamespaceInfo->RegionId = pNamespace->pParentIS->RegionId; pNamespaceInfo->Signature = NAMESPACE_INFO_SIGNATURE; InsertTailList(pNamespaceListNode, (LIST_ENTRY *) pNamespaceInfo->NamespaceInfoNode); (*pNamespacesCount)++; } ReturnCode = EFI_SUCCESS; Finish: return ReturnCode; } /** Delete namespace Deletes a block or persistent memory namespace. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] Force Force to perform deleting namespace configs on all affected DIMMs @param[in] NamespaceId the ID of the namespace to be removed. @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS if the operation was successful. @retval EFI_NOT_FOUND if a namespace with the provided GUID does not exist in the system. @retval EFI_DEVICE_ERROR if there was a problem with writing the configuration to the device. @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system. **/ EFI_STATUS EFIAPI DeleteNamespace( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN BOOLEAN Force, IN UINT16 NamespaceId, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NAMESPACE *pNamespace = NULL; UINT16 Index = 0; UINT16 RangesRemoved = 0; BOOLEAN NamespaceLocked = FALSE; NVDIMM_ENTRY(); if (pThis == NULL || pCommandStatus == NULL) { goto Finish; } CHECK_RESULT(BlockMixedSku(pCommandStatus), Finish); pNamespace = GetNamespaceById(NamespaceId); if (pNamespace == NULL) { ResetCmdStatus(pCommandStatus, NVM_ERR_NAMESPACE_DOES_NOT_EXIST); ReturnCode = EFI_NOT_FOUND; goto Finish; } ReturnCode = IsNamespaceLocked(pNamespace, &NamespaceLocked); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (NamespaceLocked) { ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_SECURITY_STATE); ReturnCode = EFI_ABORTED; goto Finish; } if (pNamespace->Flags.Values.ReadOnly) { ResetCmdStatus(pCommandStatus, NVM_ERR_NAMESPACE_READ_ONLY); ReturnCode = EFI_ABORTED; goto Finish; } if (!Force) { ResetCmdStatus(pCommandStatus, NVM_ERR_FORCE_REQUIRED); ReturnCode = EFI_ABORTED; goto Finish; } if (pNamespace->Enabled) { ReturnCode = UninstallNamespaceProtocols(pNamespace); if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_NAMESPACE_COULD_NOT_UNINSTALL); ReturnCode = EFI_ABORTED; goto Finish; } if (pNamespace->IsBttEnabled && pNamespace->pBtt != NULL) { BttRelease(pNamespace->pBtt); pNamespace->pBtt = NULL; } } for (Index = 0; Index < pNamespace->RangesCount; Index++) { NVDIMM_DBG("pDimm=%p, GUID=%g", pNamespace->Range[Index].pDimm, (GUID *) &pNamespace->NamespaceGuid); ReturnCode = RemoveNamespaceLabels(pNamespace->Range[Index].pDimm, (GUID *) &pNamespace->NamespaceGuid, 0); if (!EFI_ERROR(ReturnCode)) { RangesRemoved++; } } if (RangesRemoved == 0) { // Nothing removed, NS config structure should be still consistent ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); ReturnCode = EFI_ABORTED; goto Finish; } else if (RangesRemoved < pNamespace->RangesCount) { // Not all labels removed, NS configuration structure has been broken // NS will be removed but need to re-enumerate volumes and pools ReenumerateNamespacesAndISs(TRUE); ResetCmdStatus(pCommandStatus, NVM_ERR_NAMESPACE_CONFIGURATION_BROKEN); ReturnCode = EFI_ABORTED; } if (pNamespace->pParentIS != NULL) { RemoveEntryList(&pNamespace->IsNode); } else if (pNamespace->pParentDimm != NULL) { /** Remove Block Namespace from list of Namespaces on Dimm **/ RemoveEntryList(&pNamespace->DimmNode); } RemoveEntryList(&pNamespace->NamespaceNode); FREE_POOL_SAFE(pNamespace); SetCmdStatus(pCommandStatus, NVM_SUCCESS); ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get Error log for given dimm @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds - array of dimm pids. Use all dimms if pDimms is NULL and DimmsCount is 0. @param[in] DimmsCount - number of dimms in array. Use all dimms if pDimms is NULL and DimmsCount is 0. @param[in] ThermalError - is thermal error (if not it is media error) @param[in] SequenceNumber - sequence number of error to fetch in queue @param[in] HighLevel - high level if true, low level otherwise @param[in, out] pCount - number of error entries in output array @param[out] pErrorLogs - output array of errors @param[out] pCommandStatus Structure containing detailed NVM error codes. @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS EFIAPI GetErrorLog( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN CONST UINT32 DimmsCount, IN CONST BOOLEAN ThermalError, IN CONST UINT16 SequenceNumber, IN CONST BOOLEAN HighLevel, IN OUT UINT32 *pErrorLogCount, OUT ERROR_LOG_INFO *pErrorLogs, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; UINT32 Index = 0; UINT32 SingleDimmErrorsFetched = 0; UINT32 AllErrorsFetched = 0; SetMem(pDimms, sizeof(pDimms), 0x0); NVDIMM_ENTRY(); if (pThis == NULL || pCommandStatus == NULL || pErrorLogCount == NULL || pErrorLogs == NULL || (pDimmIds == NULL && DimmsCount > 0)) { ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_PARAMETER); goto Finish; } /** Verify input parameters and determine a list of DIMMs **/ ReturnCode = VerifyTargetDimms(pDimmIds, DimmsCount, NULL, 0, REQUIRE_DCPMMS_MANAGEABLE, pDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } ResetCmdStatus(pCommandStatus, NVM_SUCCESS); for (Index = 0; Index < DimmsNum && AllErrorsFetched < *pErrorLogCount; ++Index) { ReturnCode = GetAndParseFwErrorLogForDimm(pDimms[Index], ThermalError, HighLevel, SequenceNumber, (*pErrorLogCount - AllErrorsFetched), &SingleDimmErrorsFetched, &pErrorLogs[AllErrorsFetched]); if (EFI_ERROR(ReturnCode)) { goto Finish; } AllErrorsFetched += SingleDimmErrorsFetched; } Finish: if (pErrorLogCount != NULL) { *pErrorLogCount = AllErrorsFetched; } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get the debug log from a specified dimm and fw debug log source @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmID identifier of what dimm to get log pages from @param[in] LogSource debug log source buffer to retrieve @param[in] Reserved for future use. Must be 0 for now. @param[out] ppDebugLogBuffer - an allocated buffer containing the raw debug log @param[out] pDebugLogBufferSize - the size of the raw debug log buffer @param[out] pCommandStatus structure containing detailed NVM error codes Note: The caller is responsible for freeing the returned buffer @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS EFIAPI GetFwDebugLog( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, IN UINT8 LogSource, IN UINT32 Reserved, OUT VOID **ppDebugLogBuffer, OUT UINTN *pDebugLogBufferSize, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimm = NULL; NVDIMM_ENTRY(); if (pThis == NULL || ppDebugLogBuffer == NULL || pDebugLogBufferSize == NULL || pCommandStatus == NULL || Reserved != 0 || LogSource > FW_DEBUG_LOG_SOURCE_MAX) { ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_PARAMETER); goto Finish; } pDimm = GetDimmByPid(DimmID, &gNvmDimmData->PMEMDev.Dimms); // If we still can't find the dimm, fail out if (pDimm == NULL) { ResetCmdStatus(pCommandStatus, NVM_ERR_DIMM_NOT_FOUND); goto Finish; } if (!IsDimmManageable(pDimm)) { SetObjStatus(pCommandStatus, pDimm->DeviceHandle.AsUint32, NULL, 0, NVM_ERR_MANAGEABLE_DIMM_NOT_FOUND, ObjectTypeDimm); goto Finish; } ReturnCode = FwCmdGetFwDebugLog(pDimm, LogSource, ppDebugLogBuffer, pDebugLogBufferSize, pCommandStatus); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_SECURITY_VIOLATION) { SetObjStatusForDimm(pCommandStatus, pDimm, NVM_ERR_FW_DBG_LOG_FAILED_TO_GET_SIZE); } else if (ReturnCode == EFI_NO_MEDIA) { SetObjStatusForDimm(pCommandStatus, pDimm, NVM_ERR_MEDIA_DISABLED); } else { SetObjStatusForDimm(pCommandStatus, pDimm, NVM_ERR_OPERATION_FAILED); } goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Set Optional Configuration Data Policy using FW command @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds - pointer to array of UINT16 PMem module ids to set @param[in] DimmIdsCount - number of elements in pDimmIds @param[in] Reserved @param[in] AveragePowerReportingTimeConstant - (FIS 2.1 and greater) AveragePowerReportingTimeConstant value to set @param[out] pCommandStatus Structure containing detailed NVM error codes. @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok @retval EFI_NO_RESPONSE FW busy for one or more PMem modules **/ EFI_STATUS EFIAPI SetOptionalConfigurationDataPolicy( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT8 *Reserved, IN UINT32 *pAveragePowerReportingTimeConstant, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; PT_OPTIONAL_DATA_POLICY_PAYLOAD OptionalDataPolicyPayload; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; UINT32 Index = 0; UINT8 DimmARSStatus = 0; SetMem(pDimms, sizeof(pDimms), 0x0); ZeroMem(&OptionalDataPolicyPayload, sizeof(OptionalDataPolicyPayload)); NVDIMM_ENTRY(); if (pThis == NULL || pCommandStatus == NULL || (pDimmIds == NULL && DimmIdsCount > 0)) { ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_PARAMETER); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } CHECK_RESULT(BlockMixedSku(pCommandStatus), Finish); ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, NULL, 0, REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_FUNCTIONAL, pDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = FwCmdGetOptionalConfigurationDataPolicy(pDimms[Index], &OptionalDataPolicyPayload); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_DEVICE_ERROR; goto Finish; } if (NULL != pAveragePowerReportingTimeConstant) { OptionalDataPolicyPayload.Payload.Fis_2_01.AveragePowerReportingTimeConstant = *pAveragePowerReportingTimeConstant; } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_NOT_SUPPORTED); continue; } ReturnCode = FwCmdGetARS(pDimms[Index], &DimmARSStatus); if (LONG_OP_STATUS_IN_PROGRESS == DimmARSStatus) { NVDIMM_ERR("ARS in progress.\n"); SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_ARS_IN_PROGRESS); pCommandStatus->GeneralStatus = NVM_ERR_ARS_IN_PROGRESS; ReturnCode = EFI_DEVICE_ERROR; goto Finish; } ReturnCode = FwCmdSetOptionalConfigurationDataPolicy(pDimms[Index], &OptionalDataPolicyPayload); if (EFI_ERROR(ReturnCode)) { if (ReturnCode == EFI_SECURITY_VIOLATION) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_SECURITY_STATE); } else if (ReturnCode == EFI_NO_RESPONSE) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_BUSY_DEVICE); } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_FW_SET_OPTIONAL_DATA_POLICY_FAILED); } } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_SUCCESS); } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get requested number of specific DIMM registers for given DIMM id @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmId - ID of a DIMM. @param[out] pBsr - Pointer to buffer for Boot Status register, contains high and low 4B register. @param[out] Reserved @param[out] pCommandStatus Structure containing detailed NVM error codes. @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS EFIAPI RetrieveDimmRegisters( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmId, OUT UINT64 *pBsr, OUT UINT8 *Reserved, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; UINT16 BootStatusBitmask = 0; NVDIMM_ENTRY(); if (pThis == NULL || pBsr == NULL || pCommandStatus == NULL) { ReturnCode = EFI_INVALID_PARAMETER; ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_PARAMETER); goto Finish; } CHECK_RESULT(VerifyTargetDimms(&DimmId, 1, NULL, 0, REQUIRE_DCPMMS_MANAGEABLE, pDimms, &DimmsNum, pCommandStatus), Finish); ReturnCode = pThis->GetBSRAndBootStatusBitMask(pThis, pDimms[0]->DimmID, pBsr, &BootStatusBitmask); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_ABORTED; ResetCmdStatus(pCommandStatus, NVM_ERR_FAILED_TO_GET_DIMM_REGISTERS); goto Finish; } ResetCmdStatus(pCommandStatus, NVM_SUCCESS); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Pass Through command to FW Sends a command to FW and waits for response from firmware @param[in,out] pCmd A firmware command structure @param[in] Timeout The timeout, in 100ns units, to use for the execution of the protocol command. A Timeout value of 0 means that this function will wait indefinitely for the protocol command to execute. If Timeout is greater than zero, then this function will return EFI_TIMEOUT if the time required to execute the receive data command is greater than Timeout. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid FW Command Parameter. @retval EFI_NOT_FOUND The driver could not find the encoded in pCmd DIMM in the system. @retval EFI_DEVICE_ERROR FW error received. @retval EFI_UNSUPPORTED if the command is ran not in the DEBUG version of the driver. @retval EFI_TIMEOUT A timeout occurred while waiting for the protocol command to execute. **/ EFI_STATUS EFIAPI PassThruCommand( IN OUT NVM_FW_CMD *pCmd, IN UINT64 Timeout ) { #ifdef MDEPKG_NDEBUG return EFI_UNSUPPORTED; #else /* MDEPKG_NDEBUG */ EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pDimm = GetDimmByPid(pCmd->DimmID, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL || !IsDimmManageable(pDimm)) { NVDIMM_DBG("Could not find the specified DIMM or it is unmanageable."); ReturnCode = EFI_NOT_FOUND; goto Finish; } ReturnCode = PassThru(pDimm, pCmd, Timeout); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; #endif } EFI_STATUS EFIAPI DimmFormat( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, IN BOOLEAN Recovery, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_STATUS *pReturnCodes = NULL; #ifdef FORMAT_SUPPORTED DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; UINT32 Index = 0; DIMM_BSR Bsr; #endif NVDIMM_ENTRY(); if (pThis == NULL || pCommandStatus == NULL) { ReturnCode = EFI_INVALID_PARAMETER; ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_PARAMETER); goto Finish; } #ifndef FORMAT_SUPPORTED ReturnCode = EFI_UNSUPPORTED; ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_NOT_SUPPORTED); #else ZeroMem(pDimms, sizeof(pDimms)); ZeroMem(&Bsr, sizeof(Bsr)); ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, NULL, 0, REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_FUNCTIONAL, pDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode) || pCommandStatus->GeneralStatus != NVM_ERR_OPERATION_NOT_STARTED) { goto Finish; } pReturnCodes = AllocateZeroPool(sizeof(EFI_STATUS) * DimmsNum); if (!pReturnCodes) { ReturnCode = EFI_OUT_OF_RESOURCES; ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_NOT_STARTED); goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { pReturnCodes[Index] = FwCmdFormatDimm(pDimms[Index]); } #ifndef OS_BUILD UINT64 FwMailboxStatus = 0; /* If previous timeout then go back and wait max format time */ for (Index = 0; Index < DimmsNum; Index++) { if (pReturnCodes[Index] == EFI_TIMEOUT) { if (Recovery) { pReturnCodes[Index] = PollSmbusCmdCompletion(pDimms[Index]->SmbusAddress, PT_FORMAT_DIMM_MAX_TIMEOUT, &FwMailboxStatus, &Bsr); } if (EFI_ERROR(pReturnCodes[Index])) { continue; } } } #endif for (Index = 0; Index < DimmsNum; Index++) { if (!EFI_ERROR(pReturnCodes[Index])) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_SUCCESS); } else if (pReturnCodes[Index] == EFI_TIMEOUT) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_TIMEOUT); } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED); } KEEP_ERROR(ReturnCode,pReturnCodes[Index]); } #endif /* MDEPKG_NDEBUG */ Finish: FREE_POOL_SAFE(pReturnCodes); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Parse ACPI tables and create DIMM list @retval EFI_SUCCESS Success @retval EFI_... Other errors from subroutines **/ EFI_STATUS FillDimmList( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; EFI_STATUS TmpReturnCode = EFI_SUCCESS; NvmStatusCode StatusCode = NVM_SUCCESS; LIST_ENTRY *pNode = NULL; DIMM *pCurDimm = NULL; DIMM *pFirstDimm = NULL; UINT32 ListSize = 0; NVDIMM_ENTRY(); /* initialize the dimm inventory from the ACPI tables */ TmpReturnCode = InitializeDimmInventory(&gNvmDimmData->PMEMDev); if (EFI_ERROR(TmpReturnCode)) { NVDIMM_WARN("Initialization of one or more Dimms failed."); ReturnCode = TmpReturnCode; /** Continue even if error occurs **/ } TmpReturnCode = GetListSize(&gNvmDimmData->PMEMDev.Dimms, &ListSize); if (EFI_ERROR(TmpReturnCode)) { ReturnCode = TmpReturnCode; goto Finish; } if (ListSize == 0) { // Can't do much with 0 PMem modules! NVDIMM_DBG("Found 0 DCPMMs"); ReturnCode = EFI_NOT_FOUND; goto Finish; } gNvmDimmData->PMEMDev.DimmSkuConsistency = TRUE; pFirstDimm = DIMM_FROM_NODE(GetFirstNode(&gNvmDimmData->PMEMDev.Dimms)); LIST_FOR_EACH(pNode, &gNvmDimmData->PMEMDev.Dimms) { pCurDimm = DIMM_FROM_NODE(pNode); StatusCode = IsDimmSkuModeMismatch(pFirstDimm, pCurDimm); if (StatusCode != NVM_SUCCESS) { gNvmDimmData->PMEMDev.DimmSkuConsistency = FALSE; pCurDimm->MixedSKUOffender = TRUE; } } NVDIMM_DBG("Found %d DCPMMs", ListSize); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Clean up the in memory DIMM inventory @retval EFI_SUCCESS Success @retval EFI_... Other errors from subroutines **/ EFI_STATUS FreeDimmList( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RemoveDimmInventory(&gNvmDimmData->PMEMDev); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to remove dimm inventory."); } if (gNvmDimmData->PMEMDev.pFitHead != NULL) { FreeParsedNfit(&gNvmDimmData->PMEMDev.pFitHead); gNvmDimmData->PMEMDev.pFitHead = NULL; } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get Total DCPMM Volatile, AppDirect, Unconfigured, Reserved and Inaccessible capacities @param[in] pDimms The head of the dimm list @param[out] pRawCapacity pointer to raw capacity @param[out] pVolatileCapacity pointer to volatile capacity @param[out] pAppDirectCapacity pointer to appdirect capacity @param[out] pUnconfiguredCapacity pointer to unconfigured capacity @param[out] pReservedCapacity pointer to reserved capacity @param[out] pInaccessibleCapacity pointer to inaccessible capacity @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_LOAD_ERROR PCD CCUR table missing in one or more DIMMs @retval EFI_SUCCESS Success **/ EFI_STATUS GetTotalDcpmmCapacities( IN LIST_ENTRY *pDimms, OUT UINT64 *pRawCapacity, OUT UINT64 *pVolatileCapacity, OUT UINT64 *pAppDirectCapacity, OUT UINT64 *pUnconfiguredCapacity, OUT UINT64 *pReservedCapacity, OUT UINT64 *pInaccessibleCapacity ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; UINT64 RawCapacity = 0; UINT64 VolatileCapacity = 0; UINT64 AppDirectCapacity = 0; UINT64 UnconfiguredCapacity = 0; UINT64 ReservedCapacity = 0; UINT64 InaccessibleCapacity = 0; BOOLEAN ArgumentsNotNull = FALSE; NVDIMM_ENTRY(); if (pDimms == NULL || pRawCapacity == NULL || pVolatileCapacity == NULL || pUnconfiguredCapacity == NULL || pReservedCapacity == NULL || pAppDirectCapacity == NULL || pInaccessibleCapacity == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ArgumentsNotNull = TRUE; // All shall be zero to start *pRawCapacity = *pUnconfiguredCapacity = *pAppDirectCapacity = *pReservedCapacity = *pInaccessibleCapacity = *pVolatileCapacity = 0; LIST_FOR_EACH(pDimmNode, pDimms) { pDimm = DIMM_FROM_NODE(pDimmNode); ReturnCode = GetDcpmmCapacities(pDimm->DimmID, &RawCapacity, &VolatileCapacity, &AppDirectCapacity, &UnconfiguredCapacity, &ReservedCapacity, &InaccessibleCapacity); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to retrieve capacities for DIMM: 0x%04x", pDimm->DeviceHandle.AsUint32); goto Finish; } *pRawCapacity += RawCapacity; *pVolatileCapacity += VolatileCapacity; *pReservedCapacity += ReservedCapacity; *pAppDirectCapacity += AppDirectCapacity; *pInaccessibleCapacity += InaccessibleCapacity; *pUnconfiguredCapacity += UnconfiguredCapacity; } Finish: if (EFI_ERROR(ReturnCode) && ArgumentsNotNull == TRUE) { // Data is invalid, set to unknown *pRawCapacity = *pUnconfiguredCapacity = *pAppDirectCapacity = *pReservedCapacity = *pInaccessibleCapacity = *pVolatileCapacity = ACPI_TABLE_VALUE_UNKNOWN; } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Gather capacities from dimm @param[in] DimmPid The ID of the DIMM @param[out] pRawCapacity pointer to raw capacity @param[out] pVolatileCapacity pointer to volatile capacity @param[out] pAppDirectCapacity pointer to appdirect capacity @param[out] pUnconfiguredCapacity pointer to unconfigured capacity @param[out] pReservedCapacity pointer to reserved capacity @param[out] pInaccessibleCapacity pointer to inaccessible capacity @retval EFI_INVALID_PARAMETER passed NULL argument @retval Other errors failure of FW commands @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI GetDcpmmCapacities( IN UINT16 DimmPid, OUT UINT64 *pRawCapacity, OUT UINT64 *pVolatileCapacity, OUT UINT64 *pAppDirectCapacity, OUT UINT64 *pUnconfiguredCapacity, OUT UINT64 *pReservedCapacity, OUT UINT64 *pInaccessibleCapacity ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; MEMORY_MODE CurrentMode = MEMORY_MODE_1LM; NVDIMM_ENTRY(); pDimm = GetDimmByPid(DimmPid, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL || pRawCapacity == NULL || pVolatileCapacity == NULL || pUnconfiguredCapacity == NULL || pReservedCapacity == NULL || pAppDirectCapacity == NULL || pInaccessibleCapacity == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // All shall be zero to start *pRawCapacity = *pUnconfiguredCapacity = *pAppDirectCapacity = *pReservedCapacity = *pInaccessibleCapacity = *pVolatileCapacity = 0; *pRawCapacity = pDimm->RawCapacity; if (!IsDimmManageable(pDimm) || DIMM_MEDIA_NOT_ACCESSIBLE(pDimm->BootStatusBitmask)) { *pInaccessibleCapacity = pDimm->RawCapacity; goto Finish; } #ifdef OS_BUILD ReturnCode = GetDimmMappedMemSize(pDimm); if (EFI_DEVICE_ERROR == ReturnCode) { NVDIMM_WARN("Failed to retrieve PCD data on DIMM: 04x%x", pDimm->DeviceHandle.AsUint32); } else if (EFI_ERROR(ReturnCode)) { goto Finish; } #endif // OS_BUILD // PCD CCUR table missing in DIMM if (pDimm->ConfigStatus == DIMM_CONFIG_UNDEFINED) { ReturnCode = EFI_LOAD_ERROR; goto Finish; } ReturnCode = CurrentMemoryMode(&CurrentMode); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to determine current memory mode"); goto Finish; } if (pDimm->NonFunctional || IsDimmInUnmappedPopulationViolation(pDimm)) { // DIMM not mapped into SPA space *pInaccessibleCapacity = pDimm->RawCapacity; // No usable capacity *pAppDirectCapacity = *pReservedCapacity = *pUnconfiguredCapacity = *pVolatileCapacity = 0; goto Finish; } else if (!IS_BIOS_VOLATILE_MEMORY_MODE_2LM(CurrentMode) && !pDimm->Configured) { //DIMM is unconfigured and system is in 1LM mode *pUnconfiguredCapacity = pDimm->RawCapacity; // No usable capacity *pAppDirectCapacity = *pReservedCapacity = *pInaccessibleCapacity = *pVolatileCapacity = 0; goto Finish; } else { // Any capacity not mapped to a partition *pInaccessibleCapacity = pDimm->RawCapacity - pDimm->VolatileCapacity - pDimm->PmCapacity; } // Calculate Volatile Capacity if ((pDimm->SkuInformation.MemoryModeEnabled == MODE_ENABLED) && IS_BIOS_VOLATILE_MEMORY_MODE_2LM(CurrentMode)) { *pVolatileCapacity = pDimm->MappedVolatileCapacity; *pInaccessibleCapacity += pDimm->VolatileCapacity - pDimm->MappedVolatileCapacity; } else { // 1LM so none of the partitioned volatile is mapped. Set it as inaccessible. *pInaccessibleCapacity += pDimm->VolatileCapacity; } // Calculate AppDirect Capacity if (pDimm->SkuInformation.AppDirectModeEnabled == MODE_ENABLED) { *pAppDirectCapacity = pDimm->MappedPersistentCapacity; // Calculate Reserved Capacity ReturnCode = GetReservedCapacity(pDimm, pReservedCapacity); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to retrieve DCPMM reserved capacity."); goto Finish; } // PM partition inaccessible due to alignment/rounding *pInaccessibleCapacity += pDimm->PmCapacity - pDimm->MappedPersistentCapacity - *pReservedCapacity; } else { *pInaccessibleCapacity += pDimm->PmCapacity; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Calculate the number of channels with at least one usable DDR for 1LM+2LM @param[in] SocketId Socket Id @param[out] pChannelCount Pointer to Channel Count @retval EFI_INVALID_PARAMETER Passed NULL argument @retval EFI_LOAD_ERROR Failure to calculate DDR memory size @retval EFI_SUCCESS Success **/ EFI_STATUS GetChannelCountWithUsableDDRCache( IN UINT16 SocketId, OUT UINT32 *pChannelCount ) { EFI_STATUS ReturnCode = EFI_LOAD_ERROR; DIMM *pDimm = NULL; UINT32 ChannelCount = 0; UINT32 Socket = MAX_UINT32_VALUE; UINT32 Die = MAX_UINT32_VALUE; UINT32 MemController = MAX_UINT32_VALUE; UINT32 Channel = MAX_UINT32_VALUE; UINT32 Index1 = 0, Index2 = 0; BOOLEAN CrossTileCachingSupported = FALSE; ParsedPmttHeader *pPmttHead = NULL; NVDIMM_ENTRY(); if (NULL == pChannelCount) { NVDIMM_DBG("Null pointer passed."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = CheckIsCrossTileCachingSupported(&CrossTileCachingSupported); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to determine if cross-tile caching supported."); goto Finish; } pPmttHead = gNvmDimmData->PMEMDev.pPmttHead; // Only 0.2 is parsed and placed here if (NULL == pPmttHead) { NVDIMM_DBG("Pmtt head not found."); ReturnCode = EFI_NOT_FOUND; goto Finish; } // Enlist all channels having at least one DDR paired with a DCPMM on an iMC for (Index1 = 0; Index1 < pPmttHead->DDRModulesNum; Index1++) { if (SocketId != SOCKET_ID_ALL && pPmttHead->ppDDRModules[Index1]->SocketId != SocketId) { continue; } // Check for only 1 DDR per Channel if ((Socket == pPmttHead->ppDDRModules[Index1]->SocketId) && (Die == pPmttHead->ppDDRModules[Index1]->DieId) && (MemController == pPmttHead->ppDDRModules[Index1]->MemControllerId) && (Channel == pPmttHead->ppDDRModules[Index1]->ChannelId)) { continue; } // Taking advantage of the fact that DDRs are stored in the array in the consecutive order of socket, die, iMc, channel & slot Channel = pPmttHead->ppDDRModules[Index1]->ChannelId; MemController = pPmttHead->ppDDRModules[Index1]->MemControllerId; Die = pPmttHead->ppDDRModules[Index1]->DieId; Socket = pPmttHead->ppDDRModules[Index1]->SocketId; if (CrossTileCachingSupported) { ChannelCount++; } else { // Check to see if this DDR is paired with a DCPMM on the iMc for (Index2 = 0; Index2 < pPmttHead->DCPMModulesNum; Index2++) { pDimm = GetDimmByPid(pPmttHead->ppDCPMModules[Index2]->SmbiosHandle, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL) { NVDIMM_DBG("Failed to retrieve the DCPMM pid %x", pPmttHead->ppDCPMModules[Index2]->SmbiosHandle); goto Finish; } // Unmanageable, non-functional and population violation DCPMMs excluded for Memory Mode if (!IsDimmManageable(pDimm) || pDimm->NonFunctional || IsDimmInPopulationViolation(pDimm)) { continue; } if ((Socket == pPmttHead->ppDCPMModules[Index2]->SocketId) && (Die == pPmttHead->ppDCPMModules[Index2]->DieId) && (MemController == pPmttHead->ppDCPMModules[Index2]->MemControllerId)) { ChannelCount++; break; } } } } *pChannelCount = ChannelCount; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Calculate the total unusable DDR Cache Size in bytes for 2LM @param[in] SocketId Socket Id, 0xFFFF indicates all sockets @param[out] pTotalUnusableDDRCacheSize Pointer to total unusable DDR cache size for 2LM @retval EFI_INVALID_PARAMETER Passed NULL argument @retval EFI_LOAD_ERROR Failure to calculate total unusable DDR Cache Size @retval EFI_SUCCESS Success **/ EFI_STATUS GetUnusableDDRCacheSizeFor2LM( IN UINT16 SocketId, OUT UINT64 *pTotalUnusableDDRCacheSize ) { EFI_STATUS ReturnCode = EFI_LOAD_ERROR; DIMM *pDimm = NULL; DIMM_INFO *pDimmInfo = NULL; UINT32 Index1 = 0, Index2 = 0; BOOLEAN IsDdrPairedWithDcpmm = FALSE; BOOLEAN CrossTileCachingSupported = FALSE; BOOLEAN NonPorCrossTileSupportedConfig = FALSE; UINT32 NumOfDdrPairedWithDcpmm = 0; UINT64 TotalUnusableDDRCacheSize = 0; UINT64 TotalDDRCapacity = 0; NVDIMM_ENTRY(); if (NULL == pTotalUnusableDDRCacheSize) { NVDIMM_DBG("Null pointer passed."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pTotalUnusableDDRCacheSize = 0; pDimmInfo = (DIMM_INFO *)AllocateZeroPool(sizeof(*pDimmInfo)); if (pDimmInfo == NULL) { NVDIMM_WARN("Memory allocation error"); goto Finish; } ReturnCode = CheckIsCrossTileCachingSupported(&CrossTileCachingSupported); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to determine if cross-tile caching supported."); goto Finish; } ReturnCode = CheckIsNonPorCrossTileSupportedConfig(&NonPorCrossTileSupportedConfig); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CheckIsNonPorCrossTileSupportedConfig failed."); goto Finish; } ParsedPmttHeader *pPmttHead = NULL; pPmttHead = gNvmDimmData->PMEMDev.pPmttHead; if (NULL == pPmttHead) { NVDIMM_DBG("Pmtt head not found."); // Assuming all DDRs can be used as cache on Purley platforms (PMTT Rev: 0x1) // pPmttHead is NULL for older PMTT revisions ReturnCode = EFI_SUCCESS; goto Finish; } for (Index1 = 0; Index1 < pPmttHead->DDRModulesNum; Index1++) { if (SocketId != SOCKET_ID_ALL && pPmttHead->ppDDRModules[Index1]->SocketId != SocketId) { continue; } pDimmInfo->DimmID = pPmttHead->ppDDRModules[Index1]->SmbiosHandle; ReturnCode = FillSmbiosInfo(pDimmInfo); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error in retrieving Information from SMBIOS tables"); goto Finish; } TotalDDRCapacity += pDimmInfo->CapacityFromSmbios; // Check to see if this DDR is paired with a PMem module on the iMc for (Index2 = 0; Index2 < pPmttHead->DCPMModulesNum; Index2++) { pDimm = GetDimmByPid(pPmttHead->ppDCPMModules[Index2]->SmbiosHandle, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL) { NVDIMM_DBG("Failed to retrieve the DCPMM pid %x", pPmttHead->ppDCPMModules[Index2]->SmbiosHandle); goto Finish; } // Unmanageable, non-functional and population violation PMem modules excluded for Memory Mode if (!IsDimmManageable(pDimm) || pDimm->NonFunctional || IsDimmInPopulationViolation(pDimm)) { continue; } if ((pPmttHead->ppDDRModules[Index1]->SocketId == pPmttHead->ppDCPMModules[Index2]->SocketId) && (pPmttHead->ppDDRModules[Index1]->DieId == pPmttHead->ppDCPMModules[Index2]->DieId) && (pPmttHead->ppDDRModules[Index1]->MemControllerId == pPmttHead->ppDCPMModules[Index2]->MemControllerId)) { IsDdrPairedWithDcpmm = TRUE; NumOfDdrPairedWithDcpmm++; break; } } if (!IsDdrPairedWithDcpmm && !CrossTileCachingSupported) { TotalUnusableDDRCacheSize += pDimmInfo->CapacityFromSmbios; } IsDdrPairedWithDcpmm = FALSE; } /** Except in the case of a non-por cross-tile supported configuration, there should be at least one DDR paired with a PMem module on any iMC for cross-tile caching. **/ if (NumOfDdrPairedWithDcpmm == 0 && !NonPorCrossTileSupportedConfig) { *pTotalUnusableDDRCacheSize = TotalDDRCapacity; } else { *pTotalUnusableDDRCacheSize = TotalUnusableDDRCacheSize; } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pDimmInfo); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Calculate the total DDR Cache Size @param[in] SocketId Socket Id @param[in] VolatileMode BIOS Volatile Mode @param[out] pTotalDDRCacheSize Pointer to total DDR Cache Size @retval EFI_INVALID_PARAMETER Passed NULL argument @retval EFI_NOT_FOUND Failure Unsupported VolatileMode @retval EFI_UNSUPPORTED Failure to calculate total DDR Cache Size @retval EFI_SUCCESS Success **/ EFI_STATUS GetTotalUsableDDRCacheSize( IN UINT16 SocketId, IN MEMORY_MODE VolatileMode, OUT UINT64 *pTotalDDRCacheSize ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; UINT64 DDRCacheSizePerChannel = 0; UINT32 ChannelCount = 0; UINT64 DDRPhysicalSize = 0; UINT64 TotalDDRCacheSize = 0; UINT64 UnusableDDRCacheSize = 0; NVDIMM_ENTRY(); if (pTotalDDRCacheSize == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (MEMORY_MODE_2LM == VolatileMode) { ReturnCode = GetDDRPhysicalSize(SocketId, &DDRPhysicalSize); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Could not retrieve memory capacity."); goto Finish; } ReturnCode = GetUnusableDDRCacheSizeFor2LM(SocketId, &UnusableDDRCacheSize); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Could not retrieve memory capacity."); goto Finish; } TotalDDRCacheSize = DDRPhysicalSize - UnusableDDRCacheSize; } else if (MEMORY_MODE_1LM_PLUS_2LM == VolatileMode) { ReturnCode = RetrievePcatDDRCacheSize(&DDRCacheSizePerChannel); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Could not retrieve DDR cache size."); goto Finish; } ReturnCode = GetChannelCountWithUsableDDRCache(SocketId, &ChannelCount); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("GetChannelCountWithUsableDDRCache failed."); goto Finish; } TotalDDRCacheSize = DDRCacheSizePerChannel * ChannelCount; } else if (MEMORY_MODE_1LM == VolatileMode) { TotalDDRCacheSize = 0; } else { ReturnCode = EFI_UNSUPPORTED; NVDIMM_DBG("Unsupported mode discovered."); goto Finish; } *pTotalDDRCacheSize = TotalDDRCacheSize; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve and calculate DDR cache and memory capacity to return. @param[in] SocketId Socket Id, value 0xFFFF indicates include all socket values @param[out] pDDRRawCapacity Pointer to value of the total cache capacity @param[out] pDDRCacheCapacity Pointer to value of the DDR cache capacity @param[out] pDDRVolatileCapacity Pointer to value of the DDR memory capacity @param[out] pDDRInaccessibleCapacity Pointer to value of the DDR inaccessible capacity @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_DEVICE_ERROR Total DCPMM Persistent & Volatile capacity is larger than total mapped memory @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI GetDDRCapacities( IN UINT16 SocketId, OUT UINT64 *pDDRRawCapacity, OUT UINT64 *pDDRCacheCapacity OPTIONAL, OUT UINT64 *pDDRVolatileCapacity OPTIONAL, OUT UINT64 *pDDRInaccessibleCapacity OPTIONAL ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; EFI_STATUS PreservedReturnCode = EFI_SUCCESS; UINT64 SocketSkuTotalMappedMemory = 0; UINT64 DcpmmRawCapacity = 0; UINT64 DcpmmVolatileCapacity = 0; UINT64 DcpmmAppDirectCapacity = 0; UINT64 DcpmmUnconfiguredCapacity = 0; UINT64 DcpmmReservedCapacity = 0; UINT64 DcpmmInaccessibleCapacity = 0; MEMORY_MODE CurrentMode = MEMORY_MODE_1LM; NVDIMM_ENTRY(); if (pDDRRawCapacity == NULL || (pDDRInaccessibleCapacity != NULL && (pDDRCacheCapacity == NULL || pDDRVolatileCapacity == NULL))) { NVDIMM_DBG("Invalid parameter"); goto Finish; } ReturnCode = CurrentMemoryMode(&CurrentMode); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to determine current memory mode"); goto Finish; } // Get total physical size of all non-DCPMM DIMMs through PMTT // Semi-ignoring error since we expect the PMTT table to be missing // in Purley // Initialize to unknown *pDDRRawCapacity = ACPI_TABLE_VALUE_UNKNOWN; CHECK_RESULT_CONTINUE_PRESERVE_ERROR(GetDDRPhysicalSize(SocketId, pDDRRawCapacity)); // Get total DDR cache size if (pDDRCacheCapacity != NULL) { // Initialize to unknown *pDDRCacheCapacity = ACPI_TABLE_VALUE_UNKNOWN; CHECK_RESULT(RetrievePcatSocketSkuCachedMemory(SOCKET_ID_ALL, pDDRCacheCapacity), Finish); } // Get DDR volatile capacity: Subtract mapped DCPMM Persistent & Volatile capacity from total mapped memory if (pDDRVolatileCapacity != NULL) { // Initialize to unknown *pDDRVolatileCapacity = ACPI_TABLE_VALUE_UNKNOWN; CHECK_RESULT(RetrievePcatSocketSkuTotalMappedMemory(SOCKET_ID_ALL, &SocketSkuTotalMappedMemory), Finish); CHECK_RESULT(GetTotalDcpmmCapacities(&gNvmDimmData->PMEMDev.Dimms, &DcpmmRawCapacity, &DcpmmVolatileCapacity, &DcpmmAppDirectCapacity, &DcpmmUnconfiguredCapacity, &DcpmmReservedCapacity, &DcpmmInaccessibleCapacity), Finish); if ((DcpmmVolatileCapacity + DcpmmAppDirectCapacity) <= SocketSkuTotalMappedMemory) { *pDDRVolatileCapacity = SocketSkuTotalMappedMemory - DcpmmVolatileCapacity - DcpmmAppDirectCapacity; } else { NVDIMM_DBG("Total mapped DCPMM Persistent & Volatile capacity cannot be larger than total mapped memory."); if (CurrentMode != MEMORY_MODE_2LM) { ReturnCode = EFI_DEVICE_ERROR; goto Finish; } NVDIMM_DBG("But, in Memory Mode DDR volatile capacity is always 0 (it is used as cache)"); // Added to make ipmctl 3.x backwards compatible with Purley BIOS *pDDRVolatileCapacity = 0; } } // Get DDR inaccessible capacity if (pDDRInaccessibleCapacity != NULL) { // Allow for the PMTT table to be missing. Just set the derived value to unknown if (*pDDRRawCapacity == ACPI_TABLE_VALUE_UNKNOWN) { *pDDRInaccessibleCapacity = ACPI_TABLE_VALUE_UNKNOWN; } else { *pDDRInaccessibleCapacity = *pDDRRawCapacity - *pDDRVolatileCapacity - *pDDRCacheCapacity; } } ReturnCode = EFI_SUCCESS; Finish: if (EFI_ERROR(PreservedReturnCode) && !EFI_ERROR(ReturnCode)) { ReturnCode = PreservedReturnCode; } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Calculate the total size of available memory in the PMem modules according to the smbios and return the result @param[in] SocketId Socket Id, value 0xFFFF indicates include all socket values @param[out] pDDRPhysicalSize Pointer to total memory size @retval EFI_INVALID_PARAMETER Passed NULL argument @retval EFI_LOAD_ERROR Failure to calculate DDR memory size @retval EFI_SUCCESS Success **/ EFI_STATUS GetDDRPhysicalSize( IN UINT16 SocketId, OUT UINT64 *pDDRPhysicalSize ) { EFI_STATUS ReturnCode = EFI_LOAD_ERROR; TABLE_HEADER *pPmttRaw = NULL; DIMM_INFO *pDimmInfo = NULL; UINT64 TotalDDRMemorySize = 0; UINT16 Index = 0; NVDIMM_ENTRY(); if (NULL == pDDRPhysicalSize) { NVDIMM_DBG("Null pointer passed."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // If PMTT table is NULL here, that's ok, let the caller handle whether to // ignore this error or not CHECK_RESULT(GetAcpiPMTT(NULL, (VOID *)&pPmttRaw), Finish); // Get and parse raw PMTT table for 0.1 table if (IS_ACPI_REV_MAJ_0_MIN_1(pPmttRaw->Revision)) { PMTT_TABLE *pPMTT = (PMTT_TABLE *)pPmttRaw; PMTT_COMMON_HEADER *pCommonHeader = NULL; PMTT_MODULE *pModule = NULL; UINT64 Offset = 0; Offset = sizeof(pPMTT->Header) + sizeof(pPMTT->Reserved); //Iterate through the table and look for DDR Modules while (Offset < pPMTT->Header.Length) { pCommonHeader = (PMTT_COMMON_HEADER *)(((UINT8 *)pPMTT) + Offset); if (pCommonHeader->Type == PMTT_TYPE_SOCKET) { Offset += sizeof(PMTT_SOCKET) + PMTT_COMMON_HDR_LEN; } else if (pCommonHeader->Type == PMTT_TYPE_iMC) { Offset += sizeof(PMTT_iMC) + PMTT_COMMON_HDR_LEN; } else if (pCommonHeader->Type == PMTT_TYPE_MODULE) { pModule = (PMTT_MODULE *)(((UINT8 *)pPMTT) + Offset + PMTT_COMMON_HDR_LEN); if (pModule->SmbiosHandle != PMTT_INVALID_SMBIOS_HANDLE) { if (!(pCommonHeader->Flags & PMTT_DDR_DCPM_FLAG) && (pModule->SizeOfDimm > 0)) { TotalDDRMemorySize += MIB_TO_BYTES(pModule->SizeOfDimm); } } Offset += sizeof(PMTT_MODULE) + PMTT_COMMON_HDR_LEN; } } } else if (IS_ACPI_REV_MAJ_0_MIN_2(pPmttRaw->Revision)) { // Use our pre-parsed global struct derived from the raw PMTT table ParsedPmttHeader *pPmttHead = NULL; UINT32 DDRModulesNum; PMTT_MODULE_INFO **ppDDRModules = NULL; pPmttHead = gNvmDimmData->PMEMDev.pPmttHead; if (NULL == pPmttHead) { NVDIMM_DBG("Pmtt head not found."); ReturnCode = EFI_NOT_FOUND; goto Finish; } // Get list of DIMM modules from PMTT DDRModulesNum = pPmttHead->DDRModulesNum; ppDDRModules = pPmttHead->ppDDRModules; pDimmInfo = (DIMM_INFO *)AllocateZeroPool(sizeof(*pDimmInfo)); if (pDimmInfo == NULL) { NVDIMM_WARN("Memory allocation error"); goto Finish; } SetMem(pDimmInfo, sizeof(*pDimmInfo), 0); /* For every module, get its total capacity and add it to the system total capacity */ for (Index = 0; Index < DDRModulesNum; Index++) { if (SocketId != SOCKET_ID_ALL && ppDDRModules[Index]->SocketId != SocketId) { continue; } // Get dimm information from smbios handle pDimmInfo->DimmID = ppDDRModules[Index]->SmbiosHandle; ReturnCode = FillSmbiosInfo(pDimmInfo); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Smbios information could not be retrieved."); goto Finish; } // Get dimm capacity and add it to the total TotalDDRMemorySize += pDimmInfo->CapacityFromSmbios; } } // Set total to the result output *pDDRPhysicalSize = TotalDDRMemorySize; ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pDimmInfo); FREE_POOL_SAFE(pPmttRaw); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve Smbios tables dynamically, and populate Smbios table structures of type 17/20 for the specified Dimm Pid @param[in] DimmPid The ID of the DIMM @param[out] pDmiPhysicalDev Pointer to smbios table structure of type 17 @param[out] pDmiDeviceMappedAddr Pointer to smbios table structure of type 20 @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_DEVICE_ERROR Failure to retrieve SMBIOS tables from gST @retval EFI_SUCCESS Success **/ EFI_STATUS GetDmiMemdevInfo( IN UINT16 DimmPid, OUT SMBIOS_STRUCTURE_POINTER *pDmiPhysicalDev, OUT SMBIOS_STRUCTURE_POINTER *pDmiDeviceMappedAddr, OUT SMBIOS_VERSION *pSmbiosVersion ) { EFI_STATUS ReturnCode = EFI_DEVICE_ERROR; SMBIOS_STRUCTURE_POINTER SmBiosStruct; SMBIOS_STRUCTURE_POINTER BoundSmBiosStruct; NVDIMM_ENTRY(); ZeroMem(&SmBiosStruct, sizeof(SmBiosStruct)); ZeroMem(&BoundSmBiosStruct, sizeof(BoundSmBiosStruct)); if (pDmiPhysicalDev == NULL || pDmiDeviceMappedAddr == NULL || pSmbiosVersion == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } GetFirstAndBoundSmBiosStructPointer(&SmBiosStruct, &BoundSmBiosStruct, pSmbiosVersion); if (SmBiosStruct.Raw == NULL || BoundSmBiosStruct.Raw == NULL) { goto Finish; } while (SmBiosStruct.Raw < BoundSmBiosStruct.Raw) { if (SmBiosStruct.Hdr != NULL) { if (SmBiosStruct.Hdr->Type == SMBIOS_TYPE_MEM_DEV && SmBiosStruct.Hdr->Handle == DimmPid) { pDmiPhysicalDev->Raw = SmBiosStruct.Raw; } else if (SmBiosStruct.Hdr->Type == SMBIOS_TYPE_MEM_DEV_MAPPED_ADDR && SmBiosStruct.Type20->MemoryDeviceHandle == DimmPid) { pDmiDeviceMappedAddr->Raw = SmBiosStruct.Raw; } } else { NVDIMM_ERR("SmBios entry has invalid pointers set"); } ReturnCode = GetNextSmbiosStruct(&SmBiosStruct); if (EFI_ERROR(ReturnCode)) { goto Finish; } } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Free resources of PMTT_INFO list items @param[in] pPmttInfo - PMTT_INFO list that items will be freed for **/ STATIC VOID FreePmttItems( IN OUT LIST_ENTRY *pPmttInfo ) { PMTT_INFO *pPmttItem = NULL; LIST_ENTRY *pNode = NULL; LIST_ENTRY *pNext = NULL; NVDIMM_ENTRY(); if ((NULL == pPmttInfo) || (NULL == pPmttInfo->ForwardLink)){ goto Finish; } LIST_FOR_EACH_SAFE(pNode, pNext, pPmttInfo) { pPmttItem = PMTT_INFO_FROM_NODE(pNode); RemoveEntryList(pNode); FREE_POOL_SAFE(pPmttItem); } Finish: NVDIMM_EXIT(); } /** Step through PMTT and generate linked list of DIMM ID/Socket ID pairs If there is no PMTT, list will be empty @param[in] pPmttInfo - pointer to generic list head **/ STATIC EFI_STATUS MapSockets( LIST_ENTRY * pPmttInfo ) { EFI_STATUS ReturnCode; TABLE_HEADER *pPmttRaw = NULL; PMTT_TABLE *pPMTT = NULL; PMTT_INFO *DdrEntry = NULL; PMTT_COMMON_HEADER *pCommonHeader = NULL; PMTT_SOCKET *pSocket = NULL; PMTT_MODULE *pModule = NULL; ParsedPmttHeader *pPmttHead = NULL; PMTT_MODULE_INFO **ppAllMemModules[2]; UINT32 NumOfMemModules[2]; UINT64 PmttLen = 0; UINT64 Offset = 0; UINT32 Index1 = 0, Index2 = 0; UINT32 InvalidHandleCount = 0; LIST_ENTRY *pNode = NULL; if (NULL == pPmttInfo) { NVDIMM_ERR("Invalid Parameter"); return EFI_INVALID_PARAMETER; } InitializeListHead(pPmttInfo); ReturnCode = GetAcpiPMTT(NULL, (VOID *)&pPmttRaw); if (EFI_ERROR(ReturnCode)) { //error getting PMTT. Nothing to map //to see ddr4 topology in this case, do not use -socket goto Finish; } if (IS_ACPI_REV_MAJ_0_MIN_1(pPmttRaw->Revision)) { pPMTT = (PMTT_TABLE *)pPmttRaw; PmttLen = pPMTT->Header.Length; Offset = sizeof(pPMTT->Header) + sizeof(pPMTT->Reserved); //step through table and create socket list while (Offset < PmttLen) { pCommonHeader = (PMTT_COMMON_HEADER *)(((UINT8 *)pPMTT) + Offset); if (pCommonHeader->Type == PMTT_TYPE_SOCKET) { pSocket = (PMTT_SOCKET *)(((UINT8 *)pPMTT) + Offset + PMTT_COMMON_HDR_LEN); Offset += sizeof(PMTT_SOCKET) + PMTT_COMMON_HDR_LEN; } else if (pCommonHeader->Type == PMTT_TYPE_iMC) { Offset += sizeof(PMTT_iMC) + PMTT_COMMON_HDR_LEN; } else if (pCommonHeader->Type == PMTT_TYPE_MODULE) { if (NULL != pSocket) { pModule = (PMTT_MODULE *)(((UINT8 *)pPMTT) + Offset + PMTT_COMMON_HDR_LEN); if (pModule->SmbiosHandle != PMTT_INVALID_SMBIOS_HANDLE) { DdrEntry = AllocateZeroPool(sizeof(*DdrEntry)); if (DdrEntry == NULL) { NVDIMM_ERR("Out of memory"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } DdrEntry->PmttVersion.Revision.AsUint8 = PMTT_HEADER_REVISION_1; DdrEntry->DimmID = pModule->SmbiosHandle & SMBIOS_HANDLE_MASK; DdrEntry->SocketID = pSocket->SocketId; DdrEntry->Signature = PMTT_INFO_SIGNATURE; InsertTailList(pPmttInfo, &DdrEntry->PmttNode); } else { InvalidHandleCount++; } } Offset += sizeof(PMTT_MODULE) + PMTT_COMMON_HDR_LEN; } } } else if (IS_ACPI_REV_MAJ_0_MIN_2(pPmttRaw->Revision)) { // Use our pre-parsed 0.2-only global struct derived from the raw PMTT table pPmttHead = gNvmDimmData->PMEMDev.pPmttHead; if (pPmttHead == NULL) { ReturnCode = EFI_NOT_FOUND; NVDIMM_ERR("Can't find global pPmtt struct. Exiting"); goto Finish; } ppAllMemModules[0] = pPmttHead->ppDDRModules; ppAllMemModules[1] = pPmttHead->ppDCPMModules; NumOfMemModules[0] = pPmttHead->DDRModulesNum; NumOfMemModules[1] = pPmttHead->DCPMModulesNum; for (Index1 = 0; Index1 < 2; Index1++) { PMTT_MODULE_INFO **pMemModules = ppAllMemModules[Index1]; for (Index2 = 0; Index2 < NumOfMemModules[Index1]; Index2++) { DdrEntry = AllocateZeroPool(sizeof(*DdrEntry)); if (DdrEntry == NULL) { NVDIMM_ERR("Out of memory"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } DdrEntry->PmttVersion.Revision.AsUint8 = PMTT_HEADER_REVISION_2; DdrEntry->DimmID = pMemModules[Index2]->SmbiosHandle; DdrEntry->NodeControllerID = (UINT16)SOCKET_INDEX_TO_NFIT_NODE_ID(pMemModules[Index2]->SocketId); DdrEntry->SocketID = pMemModules[Index2]->SocketId; DdrEntry->PmttVersion.VendorData.DieID = pMemModules[Index2]->DieId; DdrEntry->MemControllerID = pMemModules[Index2]->MemControllerId; DdrEntry->PmttVersion.VendorData.ChannelID = pMemModules[Index2]->ChannelId; DdrEntry->PmttVersion.VendorData.SlotID = pMemModules[Index2]->SlotId; DdrEntry->Signature = PMTT_INFO_SIGNATURE; InsertTailList(pPmttInfo, &DdrEntry->PmttNode); } } } else { ReturnCode = EFI_UNSUPPORTED; goto Finish; } #ifndef MDEPKG_NDEBUG LIST_FOR_EACH(pNode, pPmttInfo) { DdrEntry = PMTT_INFO_FROM_NODE(pNode); NVDIMM_DBG("Dimm: 0x%X Socket: %d\n", DdrEntry->DimmID, DdrEntry->SocketID); } #endif // if a module was seen with an invalid handle and no modules with a valid handle were // seen then indicate a likely problem by returning EFI_NOT_FOUND if (InvalidHandleCount > 0) { // get count of items in list. Reuse Index1 to hold count Index1 = 0; LIST_COUNT(pNode, pPmttInfo, Index1); if (Index1 == 0) { ReturnCode = EFI_NOT_FOUND; goto Finish; } } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pPmttRaw); return ReturnCode; } /** Sorts the Dimm topology list by Memory Type. Promote PMem modules (type 2) over DDR4 and DDR5 memory. @param[in out] pMemType1 A pointer to the pDimmId list. @param[in out] pMemType2 A pointer to the copy of pDimmId list. @retval int returns 1,-1, 0 **/ INT32 SortDimmTopologyByMemType(VOID *ppTopologyDimm1, VOID *ppTopologyDimm2) { TOPOLOGY_DIMM_INFO *ppTopologyDimma = (TOPOLOGY_DIMM_INFO *)ppTopologyDimm1; TOPOLOGY_DIMM_INFO *ppTopologyDimmb = (TOPOLOGY_DIMM_INFO *)ppTopologyDimm2; if (ppTopologyDimma->MemoryType == ppTopologyDimmb->MemoryType) { return 0; } // Promote PMem modules (2) over DDR4 (1) and DDR5 (3) else if (ppTopologyDimmb->MemoryType == MEMORYTYPE_DCPM) { return 1; } else if (ppTopologyDimma->MemoryType == MEMORYTYPE_DCPM) { return -1; } else if (ppTopologyDimma->MemoryType < ppTopologyDimmb->MemoryType) { return 1; } else { return -1; } } /** Get system topology from SMBIOS table @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] ppTopologyDimm Structure containing information about DDR entries from SMBIOS. @param[out] pTopologyDimmsNumber Number of DDR entries found in SMBIOS. @retval EFI_SUCCESS All ok. @retval EFI_DEVICE_ERROR Unable to find SMBIOS table in system configuration tables. @retval EFI_OUT_OF_RESOURCES Problem with allocating memory **/ EFI_STATUS GetSystemTopologyFromSmbios( IN EFI_DCPMM_CONFIG2_PROTOCOL* pThis, OUT TOPOLOGY_DIMM_INFO** ppTopologyDimm, OUT UINT16* pTopologyDimmsNumber ) { EFI_STATUS ReturnCode = EFI_DEVICE_ERROR; SMBIOS_STRUCTURE_POINTER SmBiosStruct; SMBIOS_STRUCTURE_POINTER BoundSmBiosStruct; SMBIOS_VERSION SmbiosVersion; UINT8 CorrectedMemoryType; UINT16 Index = 0; UINT64 Capacity = 0; DIMM* pDimm = NULL; NVDIMM_ENTRY(); if (pThis == NULL || ppTopologyDimm == NULL || pTopologyDimmsNumber == NULL) { goto Finish; } *ppTopologyDimm = AllocateZeroPool(sizeof(**ppTopologyDimm) * MAX_DIMMS); if (*ppTopologyDimm == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ZeroMem(&SmBiosStruct, sizeof(SmBiosStruct)); ZeroMem(&BoundSmBiosStruct, sizeof(BoundSmBiosStruct)); ZeroMem(&SmbiosVersion, sizeof(SmbiosVersion)); GetFirstAndBoundSmBiosStructPointer(&SmBiosStruct, &BoundSmBiosStruct, &SmbiosVersion); if (SmBiosStruct.Raw == NULL || BoundSmBiosStruct.Raw == NULL) { goto Finish; } while (SmBiosStruct.Raw < BoundSmBiosStruct.Raw) { if ((SmBiosStruct.Hdr != NULL) && (SmBiosStruct.Hdr->Type == SMBIOS_TYPE_MEM_DEV) && ((SmBiosStruct.Type17->MemoryType == SMBIOS_MEMORY_TYPE_DDR4) || (SmBiosStruct.Type17->MemoryType == SMBIOS_MEMORY_TYPE_LOGICAL_NON_VOLATILE) || (SmBiosStruct.Type17->MemoryType == SMBIOS_MEMORY_TYPE_DCPM))) { (*ppTopologyDimm)[Index].DimmID = SmBiosStruct.Hdr->Handle; ReturnCode = GetSmbiosCapacity(SmBiosStruct.Type17->Size, SmBiosStruct.Type17->ExtendedSize, SmbiosVersion, &Capacity); (*ppTopologyDimm)[Index].VolatileCapacity = Capacity; ReturnCode = GetSmbiosString((SMBIOS_STRUCTURE_POINTER*)&SmBiosStruct.Type17, SmBiosStruct.Type17->DeviceLocator, (*ppTopologyDimm)[Index].DeviceLocator, sizeof((*ppTopologyDimm)[Index].DeviceLocator)); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to retrieve attribute pDmiPhysicalDev->Type17->DeviceLocator (" FORMAT_EFI_STATUS ")", ReturnCode); } ReturnCode = GetSmbiosString((SMBIOS_STRUCTURE_POINTER*)&SmBiosStruct.Type17, SmBiosStruct.Type17->BankLocator, (*ppTopologyDimm)[Index].BankLabel, sizeof((*ppTopologyDimm)[Index].BankLabel)); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Failed to retrieve attribute pDmiPhysicalDev->Type17->BankLocator (" FORMAT_EFI_STATUS ")", ReturnCode); } // override types to keep consistency with dimm_info values CorrectedMemoryType = SmBiosStruct.Type17->MemoryType; if ((SmBiosStruct.Type17->MemoryType == SMBIOS_MEMORY_TYPE_DDR4) && (SmBiosStruct.Type17->TypeDetail.Nonvolatile)) { CorrectedMemoryType = SMBIOS_MEMORY_TYPE_LOGICAL_NON_VOLATILE; } else { CorrectedMemoryType = SmBiosStruct.Type17->MemoryType; } switch (CorrectedMemoryType) { case SMBIOS_MEMORY_TYPE_DDR4: (*ppTopologyDimm)[Index].SocketID = INVALID_SOCKET_ID; // need PMTT to get this value (*ppTopologyDimm)[Index].MemoryType = MEMORYTYPE_DDR4; break; case SMBIOS_MEMORY_TYPE_DCPM: // fall through as both types are considered DCPM case SMBIOS_MEMORY_TYPE_LOGICAL_NON_VOLATILE: pDimm = GetDimmByPid(SmBiosStruct.Hdr->Handle, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL) { NVDIMM_WARN("Dimm ID: 0x%04x not found!", SmBiosStruct.Hdr->Handle); goto Finish; } (*ppTopologyDimm)[Index].SocketID = pDimm->SocketId; (*ppTopologyDimm)[Index].DimmHandle = pDimm->DeviceHandle.AsUint32; (*ppTopologyDimm)[Index].ChannelID = pDimm->ChannelId; (*ppTopologyDimm)[Index].SlotID = pDimm->ChannelPos; (*ppTopologyDimm)[Index].MemControllerID = pDimm->ImcId; (*ppTopologyDimm)[Index].MemoryType = MEMORYTYPE_DCPM; break; default: (*ppTopologyDimm)[Index].SocketID = INVALID_SOCKET_ID; // need PMTT to get this value (*ppTopologyDimm)[Index].MemoryType = MEMORYTYPE_UNKNOWN; break; } Index++; (*pTopologyDimmsNumber) = Index; } ReturnCode = GetNextSmbiosStruct(&SmBiosStruct); if (EFI_ERROR(ReturnCode)) { goto Finish; } } ReturnCode = BubbleSort(*ppTopologyDimm, *pTopologyDimmsNumber, sizeof(**ppTopologyDimm), SortDimmTopologyByMemType); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get system topology from PMTT table and if that fails try SMBIOS table @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] ppTopologyDimm Structure containing information about DDR4 entries from SMBIOS. @param[out] pTopologyDimmsNumber Number of DDR4 entries found in SMBIOS. @retval EFI_SUCCESS All ok. @retval EFI_DEVICE_ERROR Unable to find SMBIOS table in system configuration tables. @retval EFI_OUT_OF_RESOURCES Problem with allocating memory **/ EFI_STATUS EFIAPI GetSystemTopology( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT TOPOLOGY_DIMM_INFO **ppTopologyDimm, OUT UINT16 *pTopologyDimmsNumber ) { EFI_STATUS ReturnCode = EFI_DEVICE_ERROR; UINT16 Index = 0; LIST_ENTRY PmttInfo; LIST_ENTRY *pNode = NULL; PMTT_INFO *DdrEntry = NULL; DIMM_INFO *pDimmInfo = NULL; DIMM *pDimm = NULL; ACPI_REVISION Revision; NVDIMM_ENTRY(); ZeroMem(&PmttInfo, sizeof(PmttInfo)); if (pThis == NULL || ppTopologyDimm == NULL || pTopologyDimmsNumber == NULL) { goto Finish; } *ppTopologyDimm = AllocateZeroPool(sizeof(**ppTopologyDimm) * MAX_DIMMS); if (*ppTopologyDimm == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = MapSockets(&PmttInfo); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Problem detected with PMTT table. Attempting to retrieve information from SMBIOS."); ReturnCode = GetSystemTopologyFromSmbios(pThis, ppTopologyDimm, pTopologyDimmsNumber); goto Finish; } pDimmInfo = (DIMM_INFO *)AllocateZeroPool(sizeof(*pDimmInfo)); if (pDimmInfo == NULL) { NVDIMM_WARN("Memory allocation error"); goto Finish; } SetMem(pDimmInfo, sizeof(*pDimmInfo), 0); LIST_FOR_EACH(pNode, &PmttInfo) { DdrEntry = PMTT_INFO_FROM_NODE(pNode); pDimmInfo->DimmID = DdrEntry->DimmID; ReturnCode = FillSmbiosInfo(pDimmInfo); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error in retrieving Information from SMBIOS tables"); goto Finish; } (*ppTopologyDimm)[Index].DimmHandle = 0; // Retrieve DIMM Device Handle for Memory Type: DCPM if (pDimmInfo->MemoryType == MEMORYTYPE_DCPM) { pDimm = GetDimmByPid(DdrEntry->DimmID, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL) { NVDIMM_WARN("Dimm ID: 0x%04x not found!", DdrEntry->DimmID); goto Finish; } (*ppTopologyDimm)[Index].DimmHandle = pDimm->DeviceHandle.AsUint32; (*ppTopologyDimm)[Index].ChannelID = pDimm->ChannelId; (*ppTopologyDimm)[Index].SlotID = pDimm->ChannelPos; (*ppTopologyDimm)[Index].MemControllerID = pDimm->ImcId; } (*ppTopologyDimm)[Index].VolatileCapacity = pDimmInfo->CapacityFromSmbios; StrnCpyS((*ppTopologyDimm)[Index].DeviceLocator, DEVICE_LOCATOR_LEN, pDimmInfo->DeviceLocator, DEVICE_LOCATOR_LEN - 1); StrnCpyS((*ppTopologyDimm)[Index].BankLabel, BANKLABEL_LEN, pDimmInfo->BankLabel, BANKLABEL_LEN - 1); (*ppTopologyDimm)[Index].MemoryType = pDimmInfo->MemoryType; (*ppTopologyDimm)[Index].DimmID = DdrEntry->DimmID; (*ppTopologyDimm)[Index].NodeControllerID = SOCKET_INDEX_TO_NFIT_NODE_ID(DdrEntry->SocketID); (*ppTopologyDimm)[Index].SocketID = DdrEntry->SocketID; (*ppTopologyDimm)[Index].PmttVersion = DdrEntry->PmttVersion.Revision.AsUint8; CopyMem(&Revision, &DdrEntry->PmttVersion.Revision, sizeof(Revision)); if (IS_ACPI_REV_MAJ_0_MIN_2(Revision)) { (*ppTopologyDimm)[Index].DieID = DdrEntry->PmttVersion.VendorData.DieID; if (pDimmInfo->MemoryType == MEMORYTYPE_DDR4 || pDimmInfo->MemoryType == MEMORYTYPE_DDR5) { (*ppTopologyDimm)[Index].ChannelID = DdrEntry->PmttVersion.VendorData.ChannelID; (*ppTopologyDimm)[Index].SlotID = DdrEntry->PmttVersion.VendorData.SlotID; (*ppTopologyDimm)[Index].MemControllerID = DdrEntry->MemControllerID; } } Index++; (*pTopologyDimmsNumber) = Index; } ReturnCode = BubbleSort(*ppTopologyDimm, *pTopologyDimmsNumber, sizeof(**ppTopologyDimm), SortDimmTopologyByMemType); if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Error in sorting the DIMM topology list"); goto Finish; } ReturnCode = EFI_SUCCESS; Finish: FreePmttItems(&PmttInfo); FREE_POOL_SAFE(pDimmInfo); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get the system-wide ARS status for the persistent memory capacity of the system. In this function, the system-wide ARS status is determined based on the ARS status values for the individual DIMMs. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pARSStatus pointer to the current system ARS status. @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS EFIAPI GetARSStatus( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT8 *pARSStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimm = NULL; LIST_ENTRY *pDimmNode = NULL; UINT8 DimmARSStatus = LONG_OP_STATUS_NOT_STARTED; UINT8 ARSStatusBitmask = 0; NVDIMM_ENTRY(); if (pThis == NULL || pARSStatus == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pARSStatus = LONG_OP_STATUS_NOT_STARTED; LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (!IsDimmManageable(pDimm)) { continue; } if (pDimm->PmCapacity > 0) { ReturnCode = FwCmdGetARS(pDimm, &DimmARSStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("FwCmdGetARS failed with error " FORMAT_EFI_STATUS " for DIMM 0x%x", ReturnCode, pDimm->DeviceHandle.AsUint32); } if (DimmARSStatus == LONG_OP_STATUS_IN_PROGRESS) { *pARSStatus = LONG_OP_STATUS_IN_PROGRESS; goto Finish; break; } else { // OR into the bitmask for each PMem module so we can collectively // present the worst error at the end // Do a quick bounds check, just in case CHECK_NOT_TRUE(DimmARSStatus <= 7, Finish); ARSStatusBitmask |= 1 << DimmARSStatus; } } } // Show only the worst error in this order if (ARSStatusBitmask & (1 << LONG_OP_STATUS_UNKNOWN)) { *pARSStatus = LONG_OP_STATUS_UNKNOWN; } else if (ARSStatusBitmask & (1 << LONG_OP_STATUS_ERROR)) { *pARSStatus = LONG_OP_STATUS_ERROR; } else if (ARSStatusBitmask & (1 << LONG_OP_STATUS_ABORTED)) { *pARSStatus = LONG_OP_STATUS_ABORTED; } else if (ARSStatusBitmask & (1 << LONG_OP_STATUS_COMPLETED)) { *pARSStatus = LONG_OP_STATUS_COMPLETED; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get the User Driver Preferences. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pDriverPreferences pointer to the current driver preferences. @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS EFIAPI GetDriverPreferences( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT DRIVER_PREFERENCES *pDriverPreferences, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); /* check input parameters */ if (pThis == NULL || pDriverPreferences == NULL || pCommandStatus == NULL) { NVDIMM_DBG("One or more parameters are NULL"); ReturnCode = EFI_INVALID_PARAMETER; ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_PARAMETER); goto Finish; } ReturnCode = ReadRunTimeDriverPreferences(pDriverPreferences); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to retrieve DriverPreferences"); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } ResetCmdStatus(pCommandStatus, NVM_SUCCESS); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Set the User Driver Preferences. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDriverPreferences pointer to the desired driver preferences. @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS EFIAPI SetDriverPreferences( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN DRIVER_PREFERENCES *pDriverPreferences, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINTN VariableSize = 0; BOOLEAN Conflict = FALSE; NVDIMM_ENTRY(); /* check input parameters */ if (pThis == NULL || pDriverPreferences == NULL) { NVDIMM_DBG("One or more parameters are NULL"); ReturnCode = EFI_INVALID_PARAMETER; ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_PARAMETER); goto Finish; } ReturnCode = AppDirectSettingsValidation(pDriverPreferences); if (EFI_ERROR(ReturnCode)) { ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_PARAMETER); goto Finish; } ReturnCode = AppDirectSettingsConflict(pDriverPreferences, &Conflict, pCommandStatus); if (EFI_ERROR(ReturnCode) || Conflict) { goto Finish; } VariableSize = sizeof(pDriverPreferences->ChannelInterleaving); ReturnCode = SET_VARIABLE_NV( CHANNEL_INTERLEAVE_SIZE_VARIABLE_NAME, gNvmDimmNgnvmVariableGuid, VariableSize, &pDriverPreferences->ChannelInterleaving); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to set Channel Interleave Variable"); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } VariableSize = sizeof(pDriverPreferences->ImcInterleaving); ReturnCode = SET_VARIABLE_NV( IMC_INTERLEAVE_SIZE_VARIABLE_NAME, gNvmDimmNgnvmVariableGuid, VariableSize, &pDriverPreferences->ImcInterleaving); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to set iMC Interleave Variable"); ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_FAILED); goto Finish; } ResetCmdStatus(pCommandStatus, NVM_SUCCESS); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get DDRT IO init info @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] DimmID DimmID of device to retrieve support data from @param[out] pDdrtTrainingStatus pointer to the dimms DDRT training status @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS EFIAPI GetDdrtIoInitInfo( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT UINT8 *pDdrtTrainingStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimm = NULL; PT_OUTPUT_PAYLOAD_GET_DDRT_IO_INIT_INFO DdrtIoInitInfo; NVDIMM_ENTRY(); ZeroMem(&DdrtIoInitInfo, sizeof(DdrtIoInitInfo)); pDimm = GetDimmByPid(DimmID, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL) { goto Finish; } if (!IsDimmManageable(pDimm)) { goto Finish; } ReturnCode = FwCmdGetDdrtIoInitInfo(pDimm, &DdrtIoInitInfo); if (EFI_ERROR(ReturnCode)) { goto Finish; } *pDdrtTrainingStatus = DdrtIoInitInfo.DdrtTrainingStatus; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get long operation status @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] DimmID DimmID of device to retrieve status from @param[in] pOpcode pointer to opcode of long op command to check @param[in] pOpcode pointer to subopcode of long op command to check @param[out] pPercentComplete pointer to percentage current command has completed @param[out] pEstimatedTimeLeft pointer to time to completion BCD @param[out] pFwStatus pointer to completed mailbox status code @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS EFIAPI GetLongOpStatus( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT UINT8 *pOpcode OPTIONAL, OUT UINT8 *pSubOpcode OPTIONAL, OUT UINT16 *pPercentComplete OPTIONAL, OUT UINT32 *pEstimatedTimeLeft OPTIONAL, OUT EFI_STATUS *pLongOpEfiStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimm = NULL; UINT8 FwStatus = FW_SUCCESS; PT_OUTPUT_PAYLOAD_FW_LONG_OP_STATUS LongOpStatus; NVDIMM_ENTRY(); ZeroMem(&LongOpStatus, sizeof(LongOpStatus)); pDimm = GetDimmByPid(DimmID, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL) { goto Finish; } if (!IsDimmManageable(pDimm)) { goto Finish; } ReturnCode = FwCmdGetLongOperationStatus(pDimm, &FwStatus, &LongOpStatus); if (EFI_ERROR(ReturnCode)) { // If FIS <= 1.5, getting long op might not be supported if (((pDimm->FwVer.FwApiMajor == 1) && (pDimm->FwVer.FwApiMinor <= 5)) && ((FwStatus == FW_INTERNAL_DEVICE_ERROR) || (FwStatus == FW_DATA_NOT_SET) || (FwStatus == FW_UNSUPPORTED_COMMAND))) { ReturnCode = EFI_UNSUPPORTED; } goto Finish; } *pLongOpEfiStatus = MatchFwReturnCode(LongOpStatus.Status); NVDIMM_DBG("Long operation status converted from FW code %d to EFI code %d", LongOpStatus.Status, *pLongOpEfiStatus); if (pOpcode != NULL) { *pOpcode = LongOpStatus.CmdOpcode; } if (pSubOpcode != NULL) { *pSubOpcode = LongOpStatus.CmdSubOpcode; } if (pPercentComplete != NULL) { *pPercentComplete = LongOpStatus.Percent; } if (pEstimatedTimeLeft != NULL) { *pEstimatedTimeLeft = LongOpStatus.EstimatedTimeLeft; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Automatically provision capacity Decision logic for when to automatically provision capacity based on ProvisionCapacityStatus, PCD data and topology change. If automatic provisioning is triggered and succeeds, this function will reboot and never return. @param[in] pIntelDIMMConfig Pointer to struct containing EFI vars @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS AutomaticProvisionCapacity( IN INTEL_DIMM_CONFIG *pIntelDIMMConfig ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; BOOLEAN GoalSuccess = FALSE; BOOLEAN VarsMatch = TRUE; BOOLEAN TopologyChanged = FALSE; NVDIMM_ENTRY(); if (pIntelDIMMConfig == NULL) { goto FinishNoUpdate; } if (pIntelDIMMConfig->Revision != INTEL_DIMM_CONFIG_REVISION) { NVDIMM_DBG("Revision not supported: %d. Replacing with current supported version: %d.", pIntelDIMMConfig->Revision, INTEL_DIMM_CONFIG_REVISION); pIntelDIMMConfig->Revision = INTEL_DIMM_CONFIG_REVISION; } if (pIntelDIMMConfig->ProvisionCapacityMode != PROVISION_CAPACITY_MODE_AUTO) { // Sanity check, this should never happen pIntelDIMMConfig->ProvisionCapacityStatus = PROVISION_CAPACITY_STATUS_ERROR; ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } switch (pIntelDIMMConfig->ProvisionCapacityStatus) { case PROVISION_CAPACITY_STATUS_NEW_UNKNOWN: case PROVISION_CAPACITY_STATUS_SUCCESS: case PROVISION_CAPACITY_STATUS_ERROR: case PROVISION_CAPACITY_STATUS_PENDING_SECURITY_DISABLED: // Check PCD copy of variables ReturnCode = CheckPCDAutoConfVars(pIntelDIMMConfig, &VarsMatch); if (EFI_ERROR(ReturnCode)) { pIntelDIMMConfig->ProvisionCapacityStatus = PROVISION_CAPACITY_STATUS_ERROR; goto Finish; } // Check if the DIMM topology changed ReturnCode = CheckTopologyChange(&TopologyChanged); if (EFI_ERROR(ReturnCode)) { pIntelDIMMConfig->ProvisionCapacityStatus = PROVISION_CAPACITY_STATUS_ERROR; goto Finish; } if (!VarsMatch || TopologyChanged) { ReturnCode = AutomaticCreateGoal(pIntelDIMMConfig); if (ReturnCode == EFI_ACCESS_DENIED) { // If we already tried to signal security needs to be disabled and it was not, // error here so we don't boot loop. if (pIntelDIMMConfig->ProvisionCapacityStatus == PROVISION_CAPACITY_STATUS_PENDING_SECURITY_DISABLED) { pIntelDIMMConfig->ProvisionCapacityStatus = PROVISION_CAPACITY_STATUS_ERROR; } else { pIntelDIMMConfig->ProvisionCapacityStatus = PROVISION_CAPACITY_STATUS_PENDING_SECURITY_DISABLED; } } else if (EFI_ERROR(ReturnCode)) { pIntelDIMMConfig->ProvisionCapacityStatus = PROVISION_CAPACITY_STATUS_ERROR; } else { pIntelDIMMConfig->ProvisionCapacityStatus = PROVISION_CAPACITY_STATUS_PENDING; // Indicate namespaces have been deleted creating the new goal pIntelDIMMConfig->ProvisionNamespaceStatus = PROVISION_NAMESPACE_STATUS_NEW_UNKNOWN; } } else { pIntelDIMMConfig->ProvisionCapacityStatus = PROVISION_CAPACITY_STATUS_SUCCESS; } break; case PROVISION_CAPACITY_STATUS_PENDING: CheckGoalStatus(&GoalSuccess); if (GoalSuccess) { pIntelDIMMConfig->ProvisionCapacityStatus = PROVISION_CAPACITY_STATUS_SUCCESS; } else { pIntelDIMMConfig->ProvisionCapacityStatus = PROVISION_CAPACITY_STATUS_ERROR; goto Finish; } break; default: NVDIMM_DBG("Invalid ProvisionCapacityStatus: %d", pIntelDIMMConfig->ProvisionCapacityStatus); pIntelDIMMConfig->ProvisionCapacityStatus = PROVISION_CAPACITY_STATUS_ERROR; break; } Finish: // Update status NVDIMM_DBG("New ProvisionCapacityStatus: %d", pIntelDIMMConfig->ProvisionCapacityStatus); UpdateIntelDIMMConfig(pIntelDIMMConfig); if (pIntelDIMMConfig->ProvisionCapacityStatus == PROVISION_CAPACITY_STATUS_PENDING) { DEBUG((EFI_D_INFO, "Resetting for automatic provisioning.\n")); gRT->ResetSystem(EfiResetCold, ReturnCode, 0, NULL); } else if (pIntelDIMMConfig->ProvisionCapacityStatus == PROVISION_CAPACITY_STATUS_PENDING_SECURITY_DISABLED) { DEBUG((EFI_D_INFO, "Security enabled, resetting. Provisioning pending security disabled.\n")); gRT->ResetSystem(EfiResetCold, ReturnCode, 0, NULL); } FinishNoUpdate: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Automatically provision namespaces Decision logic for when to automatically provision namespaces based on ProvisionNamespaceStatus. @param[in] pIntelDIMMConfig Pointer to struct containing EFI vars @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS AutomaticProvisionNamespace( IN INTEL_DIMM_CONFIG *pIntelDIMMConfig ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (pIntelDIMMConfig == NULL) { goto Finish; } // Check if namespace provisioning is needed if ((pIntelDIMMConfig->ProvisionNamespaceStatus == PROVISION_NAMESPACE_STATUS_NEW_UNKNOWN) && (pIntelDIMMConfig->ProvisionCapacityStatus != PROVISION_CAPACITY_STATUS_ERROR)) { ReturnCode = AutomaticCreateNamespace(pIntelDIMMConfig); if (EFI_ERROR(ReturnCode)) { pIntelDIMMConfig->ProvisionNamespaceStatus = PROVISION_NAMESPACE_STATUS_ERROR; } else { pIntelDIMMConfig->ProvisionNamespaceStatus = PROVISION_NAMESPACE_STATUS_SUCCESS; } } // Update status NVDIMM_DBG("New ProvisionNamespaceStatus: %d", pIntelDIMMConfig->ProvisionNamespaceStatus); UpdateIntelDIMMConfig(pIntelDIMMConfig); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Checks inputs and executes create goal Will remove all namespaces. @param[in] pIntelDIMMConfig Pointer to struct containing EFI vars @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS AutomaticCreateGoal( IN INTEL_DIMM_CONFIG *pIntelDIMMConfig ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; COMMAND_STATUS *pCommandStatus = NULL; UINT8 PersistentMemType = 0; UINT16 Major = 0; UINT16 Minor = 0; LIST_ENTRY NamespaceListHead = {0}; LIST_ENTRY *pNextNode = NULL; LIST_ENTRY *pNode = NULL; UINT32 NamespaceCount = 0; NAMESPACE_INFO *pCurNamespace = NULL; DIMM **ppDimms = NULL; UINT32 DimmsNum = 0; UINT32 DimmSecurityState = 0; UINT32 Index = 0; REQUIRE_DCPMMS RequireDcpmmsBitfield = REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_FUNCTIONAL; NVDIMM_ENTRY(); InitializeListHead(&NamespaceListHead); if (pIntelDIMMConfig == NULL) { goto Finish; } ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } /** Check IntelDimmConfig variables **/ if (pIntelDIMMConfig->MemorySize > MEMORY_SIZE_MAX_PERCENT) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (pIntelDIMMConfig->PMType == PM_TYPE_APPDIRECT) { PersistentMemType = PM_TYPE_AD; } else if (pIntelDIMMConfig->PMType == PM_TYPE_APPDIRECT_NOT_INTERLEAVED) { PersistentMemType = PM_TYPE_AD_NI; } else { NVDIMM_DBG("Invalid PMType: %d", pIntelDIMMConfig->PMType); ReturnCode = EFI_INVALID_PARAMETER; } ReturnCode = GetNSLabelMajorMinorVersion(pIntelDIMMConfig->NamespaceLabelVersion, &Major, &Minor); if (EFI_ERROR(ReturnCode)) { goto Finish; } ppDimms = AllocateZeroPool(sizeof(*ppDimms) * MAX_DIMMS); if (ppDimms == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } //DCPMMs in population violation are ignored from all goal requests except in the case that the goal //request is for ADx1 100%. In this case DCPMMs in population violation can be used. if (!((PM_TYPE_AD_NI == PersistentMemType) && (0 == pIntelDIMMConfig->MemorySize))) { RequireDcpmmsBitfield |= REQUIRE_DCPMMS_NO_POPULATION_VIOLATION; } ReturnCode = VerifyTargetDimms(NULL, 0, NULL, 0, RequireDcpmmsBitfield, ppDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } // Check for security for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = GetDimmSecurityState(ppDimms[Index], PT_TIMEOUT_INTERVAL, &DimmSecurityState); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (!IsConfiguringForCreateGoalAllowed(DimmSecurityState)) { ReturnCode = EFI_ACCESS_DENIED; ResetCmdStatus(pCommandStatus, NVM_ERR_CREATE_GOAL_NOT_ALLOWED); NVDIMM_DBG("Invalid request to create goal while security is in locked state."); goto Finish; } } /** Get and delete namespaces **/ ReturnCode = GetNamespaces(&gNvmDimmDriverNvmDimmConfig, &NamespaceListHead, &NamespaceCount, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } if (NamespaceCount > 0) { LIST_FOR_EACH(pNode, &NamespaceListHead) { pCurNamespace = NAMESPACE_INFO_FROM_NODE(pNode); ReturnCode = DeleteNamespace(&gNvmDimmDriverNvmDimmConfig, TRUE, pCurNamespace->NamespaceId, pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed to delete namespaces"); goto Finish; } } } ResetCmdStatus(pCommandStatus, NVM_ERR_OPERATION_NOT_STARTED); // Set Alignment to 1GiB gNvmDimmData->Alignments.RegionPartitionAlignment = SIZE_1GB; /** Run create goal **/ ReturnCode = CreateGoalConfig(&gNvmDimmDriverNvmDimmConfig, FALSE, // Not a dry run NULL, // Use all DIMMs 0, // Use all DIMMs NULL, // Use all sockets 0, // Use all sockets PersistentMemType, // Get PMType from variable pIntelDIMMConfig->MemorySize, // Get MemorySize from variable 0, // No reserved RESERVE_DIMM_NONE, // No reserve DIMM Major, // Major label version from variable Minor, // Minor label version from variable NULL, pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CreateGoalConfig failed with ReturnCode: %d", ReturnCode); goto Finish; } Finish: FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(ppDimms); LIST_FOR_EACH_SAFE(pNode, pNextNode, &NamespaceListHead) { FreePool(NAMESPACE_INFO_FROM_NODE(pNode)); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Checks inputs and executes create namespace on empty ISets @param[in] pIntelDIMMConfig Pointer to struct containing EFI vars @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS AutomaticCreateNamespace( IN INTEL_DIMM_CONFIG *pIntelDIMMConfig ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; COMMAND_STATUS *pCommandStatus = NULL; BOOLEAN BttEnabled = FALSE; UINT32 RegionCount = 0; REGION_INFO *pRegions = NULL; UINT32 Index = 0; UINT64 AdjustedCapacity = 0; UINT16 CreatedNamespaceId = 0; NVDIMM_ENTRY(); if (pIntelDIMMConfig == NULL) { goto Finish; } ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } if (pIntelDIMMConfig->NamespaceFlags == NAMESPACE_FLAG_BTT) { BttEnabled = TRUE; } else if (pIntelDIMMConfig->NamespaceFlags != NAMESPACE_FLAG_NONE) { NVDIMM_DBG("Invalid NamespaceFlags: %d", pIntelDIMMConfig->NamespaceFlags); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Find all Regions GetRegionCount(&gNvmDimmDriverNvmDimmConfig, FALSE, &RegionCount); pRegions = AllocateZeroPool(sizeof(REGION_INFO) * RegionCount); if (pRegions == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = GetRegions(&gNvmDimmDriverNvmDimmConfig, RegionCount, FALSE, pRegions, pCommandStatus); for (Index = 0; Index < RegionCount; Index++) { // Check if Region is empty if (pRegions[Index].Capacity != pRegions[Index].FreeCapacity) { NVDIMM_DBG("Region %d is not empty. Skip automatic namespace provision", pRegions[Index].RegionId); // No interleave sets to provision is success ReturnCode = EFI_SUCCESS; continue; } ReturnCode = CreateNamespace(&gNvmDimmDriverNvmDimmConfig, pRegions[Index].RegionId, // Iterate through Regions DIMM_PID_NOTSET, NAMESPACE_PM_NAMESPACE_BLOCK_SIZE, NAMESPACE_BLOCK_COUNT_UNDEFINED, // Use all free space NULL, // No name BttEnabled, // BTT from variable TRUE, // ForceAll TRUE, // ForceAlignment &AdjustedCapacity, &CreatedNamespaceId, pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Automatic ns provisioning failed. ReturnCode: %d", ReturnCode); goto Finish; } } Finish: FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pRegions); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Updates ProvisionCapacityStatus @param[in] ProvisionCapacityStatus New status to write **/ VOID UpdateIntelDIMMConfig( IN INTEL_DIMM_CONFIG *pIntelDIMMConfig ) { SET_VARIABLE_NV( INTEL_DIMM_CONFIG_VARIABLE_NAME, gIntelDimmConfigVariableGuid, sizeof(INTEL_DIMM_CONFIG), pIntelDIMMConfig); } /** Checks if PCD copy of vars matches EFI on all DIMMs If a DIMM is missing PCD data then it is considered not matching @param[in] pIntelDIMMConfig Pointer to struct with EFI vars @param[out] pVarsMatch True if PCD data on all DIMMs match @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS CheckPCDAutoConfVars( IN INTEL_DIMM_CONFIG *pIntelDIMMConfigEfiVar, OUT BOOLEAN *pVarsMatch ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; COMMAND_STATUS *pCommandStatus = NULL; NVDIMM_CONFIGURATION_HEADER *pConfHeader = NULL; NVDIMM_PLATFORM_CONFIG_INPUT *pConfigInput = NULL; CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *pConfigManagementAttributesInfo = NULL; INTEL_DIMM_CONFIG *pIntelDIMMConfigPCD = NULL; PCAT_TABLE_HEADER *pCurPcatTable = NULL; UINT32 SizeOfPcatTables = 0; UINT32 Index = 0; DIMM **ppDimms = NULL; UINT32 DimmsNum = 0; BOOLEAN MatchingTableFound = FALSE; NVDIMM_ENTRY(); if (pIntelDIMMConfigEfiVar == NULL) { goto Finish; } ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } ppDimms = AllocateZeroPool(sizeof(*ppDimms) * MAX_DIMMS); if (ppDimms == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } // Get all DIMMs ReturnCode = VerifyTargetDimms(NULL, 0, NULL, 0, REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_FUNCTIONAL, ppDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = GetPlatformConfigDataOemPartition(ppDimms[Index], FALSE, &pConfHeader); #ifdef MEMORY_CORRUPTION_WA if (ReturnCode == EFI_DEVICE_ERROR) { ReturnCode = GetPlatformConfigDataOemPartition(ppDimms[Index], FALSE, &pConfHeader); } #endif // MEMORY_CORRUPTIO_WA if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error in retrieving Input Config table"); goto Finish; } if (pConfHeader->ConfInputStartOffset == 0 || pConfHeader->ConfInputDataSize == 0) { // No input Config table NVDIMM_DBG("No input config table"); *pVarsMatch = FALSE; ReturnCode = EFI_SUCCESS; goto Finish; } pConfigInput = GET_NVDIMM_PLATFORM_CONFIG_INPUT(pConfHeader); pCurPcatTable = (PCAT_TABLE_HEADER *) &pConfigInput->pPcatTables; SizeOfPcatTables = pConfigInput->Header.Length - (UINT32) ((UINT8 *)pCurPcatTable - (UINT8 *)pConfigInput); MatchingTableFound = FALSE; while ((UINT32) ((UINT8 *) pCurPcatTable - (UINT8 *) &pConfigInput->pPcatTables) < SizeOfPcatTables) { if (pCurPcatTable->Type == PCAT_TYPE_CONFIG_MANAGEMENT_ATTRIBUTES_TABLE) { pConfigManagementAttributesInfo = (CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *) pCurPcatTable; if (CompareGuid(&pConfigManagementAttributesInfo->Guid, &gIntelDimmConfigVariableGuid)) { pIntelDIMMConfigPCD = (INTEL_DIMM_CONFIG *) pConfigManagementAttributesInfo->pGuidData; if ((pIntelDIMMConfigPCD->Revision == pIntelDIMMConfigEfiVar->Revision) && (pIntelDIMMConfigPCD->ProvisionCapacityMode == pIntelDIMMConfigEfiVar->ProvisionCapacityMode) && (pIntelDIMMConfigPCD->MemorySize == pIntelDIMMConfigEfiVar->MemorySize) && (pIntelDIMMConfigPCD->PMType == pIntelDIMMConfigEfiVar->PMType) && (pIntelDIMMConfigPCD->NamespaceLabelVersion == pIntelDIMMConfigEfiVar->NamespaceLabelVersion) && (pIntelDIMMConfigPCD->ProvisionNamespaceMode == pIntelDIMMConfigEfiVar->ProvisionNamespaceMode) && (pIntelDIMMConfigPCD->NamespaceFlags == pIntelDIMMConfigEfiVar->NamespaceFlags)) { NVDIMM_DBG("EFI Vars and PCD data match"); MatchingTableFound = TRUE; break; } } } pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, ((PCAT_TABLE_HEADER *) pCurPcatTable)->Length); } if (!MatchingTableFound) { // Did not find an extension table with guid // Or found one but with different variables NVDIMM_DBG("Matching auto conf extension table not found"); *pVarsMatch = FALSE; ReturnCode = EFI_SUCCESS; goto Finish; } } NVDIMM_DBG("All DIMM's have matching EFI vars"); *pVarsMatch = TRUE; ReturnCode = EFI_SUCCESS; Finish: FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(ppDimms); FREE_POOL_SAFE(pConfHeader); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Checks if the topology has changed based on CCUR config status @param[out] pTopologyChanged True if ConfigStatus indicates topology change @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS CheckTopologyChange( OUT BOOLEAN *pTopologyChanged ) { COMMAND_STATUS *pCommandStatus = NULL; EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; LIST_ENTRY *pDimmList = NULL; LIST_ENTRY *pCurrentDimmNode = NULL; DIMM *pCurrentDimm = NULL; NVDIMM_ENTRY(); ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } // Check if any DIMMs are non-functional // Can't check topology if a DIMM is non-functional pDimmList = &gNvmDimmData->PMEMDev.Dimms; LIST_FOR_EACH(pCurrentDimmNode, pDimmList) { pCurrentDimm = DIMM_FROM_NODE(pCurrentDimmNode); if (pCurrentDimm->NonFunctional == TRUE) { NVDIMM_ERR("Non-functional DIMM found. Cannot check topology change"); ReturnCode = EFI_ABORTED; goto Finish; } if (pCurrentDimm->ConfigStatus != DIMM_CONFIG_SUCCESS) { if ((pCurrentDimm->ConfigStatus == DIMM_CONFIG_IS_INCOMPLETE) || (pCurrentDimm->ConfigStatus == DIMM_CONFIG_NO_MATCHING_IS) || (pCurrentDimm->ConfigStatus == DIMM_CONFIG_NEW_DIMM)) { NVDIMM_DBG("Topology changed detected"); *pTopologyChanged = TRUE; ReturnCode = EFI_SUCCESS; goto Finish; } else { // Config error // This should not happen as the goal status was checked to enter this state ReturnCode = EFI_ABORTED; goto Finish; } } } // All DIMM_CONFIG_SUCCESS means no topology change NVDIMM_DBG("No Topology change detected"); *pTopologyChanged = FALSE; ReturnCode = EFI_SUCCESS; Finish: FreeCommandStatus(&pCommandStatus); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Checks if the previous goal was applied successfully @param[out] pGoalSuccess True if goal was applied successfully @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS CheckGoalStatus( OUT BOOLEAN *pGoalSuccess ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; COMMAND_STATUS *pCommandStatus = NULL; DIMM **ppDimms = NULL; UINT32 DimmsNum = 0; UINT32 Index = 0; NVDIMM_ENTRY(); ReturnCode = InitializeCommandStatus(&pCommandStatus); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed on InitializeCommandStatus"); goto Finish; } ppDimms = AllocateZeroPool(sizeof(*ppDimms) * MAX_DIMMS); if (ppDimms == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } // Get all DIMMs ReturnCode = VerifyTargetDimms(NULL, 0, NULL, 0, REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_FUNCTIONAL, ppDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } ReturnCode = RetrieveGoalConfigsFromPlatformConfigData(&gNvmDimmData->PMEMDev.Dimms, FALSE); if (EFI_ERROR(ReturnCode)) { goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { if (ppDimms[Index]->GoalConfigStatus != GOAL_CONFIG_STATUS_NO_GOAL_OR_SUCCESS) { *pGoalSuccess = FALSE; ReturnCode = EFI_ABORTED; goto Finish; } } *pGoalSuccess = TRUE; ReturnCode = EFI_SUCCESS; Finish: FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(ppDimms); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** InjectError @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds - pointer to array of UINT16 PMem module ids to get data for @param[in] DimmIdsCount - number of elements in pDimmIds @param[IN] ErrorInjType - Error Inject type @param[IN] ClearStatus - Is clear status set @param[IN] pInjectTemperatureValue - Pointer to inject temperature @param[IN] pInjectPoisonAddress - Pointer to inject poison address @param[IN] pPoisonType - Pointer to poison type @param[IN] pPercentageRemaining - Pointer to percentage remaining @param[out] pCommandStatus Structure containing detailed NVM error codes. @retval EFI_UNSUPPORTED Mixed Sku of DCPMMs has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS EFIAPI InjectError( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT8 ErrorInjType, IN UINT8 ClearStatus, IN UINT64 *pInjectTemperatureValue, IN UINT64 *pInjectPoisonAddress, IN UINT8 *pPoisonType, IN UINT8 *pPercentageRemaining, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_SUCCESS; VOID *pInputPayload = NULL; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; UINT32 Index = 0; UINT32 SecurityState = 0; PT_PAYLOAD_GET_PACKAGE_SPARING_POLICY *pPayloadPackageSparingPolicy = NULL; UINT8 FwStatus = FW_SUCCESS; UINT8 DimmARSStatus = 0; SetMem(pDimms, sizeof(pDimms), 0x0); NVDIMM_ENTRY(); if (pThis == NULL || pCommandStatus == NULL || (pDimmIds == NULL && DimmIdsCount > 0)) { ResetCmdStatus(pCommandStatus, NVM_ERR_INVALID_PARAMETER); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = VerifyTargetDimms(pDimmIds, DimmIdsCount, NULL, 0, REQUIRE_DCPMMS_MANAGEABLE | REQUIRE_DCPMMS_FUNCTIONAL, pDimms, &DimmsNum, pCommandStatus); if (EFI_ERROR(ReturnCode)) { goto Finish; } switch (ErrorInjType) { case ERROR_INJ_TEMPERATURE: pInputPayload = AllocateZeroPool(sizeof(PT_INPUT_PAYLOAD_INJECT_TEMPERATURE)); if (pInputPayload == NULL) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_NO_MEM); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } if (pInjectTemperatureValue == NULL) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_PARAMETER); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ((PT_INPUT_PAYLOAD_INJECT_TEMPERATURE *)pInputPayload)->Enable = !ClearStatus; ((PT_INPUT_PAYLOAD_INJECT_TEMPERATURE *)pInputPayload)->Temperature.Separated.TemperatureInteger = (UINT16) *pInjectTemperatureValue; for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = FwCmdInjectError(pDimms[Index], SubopMediaErrorTemperature, (VOID *)pInputPayload, &FwStatus); if (EFI_ERROR(ReturnCode)) { if (FwStatus == FW_INJECTION_NOT_ENABLED) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_ERROR_INJECTION_BIOS_KNOB_NOT_ENABLED); continue; } SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED); ReturnCode = EFI_DEVICE_ERROR; continue; } SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_SUCCESS); } break; case ERROR_INJ_PACKAGE_SPARING: pInputPayload = AllocateZeroPool(sizeof(PT_INPUT_PAYLOAD_INJECT_SW_TRIGGERS)); if (pInputPayload == NULL) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_NO_MEM); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ((PT_INPUT_PAYLOAD_INJECT_SW_TRIGGERS *)pInputPayload)->TriggersToModify = PACKAGE_SPARING_TRIGGER; ((PT_INPUT_PAYLOAD_INJECT_SW_TRIGGERS *)pInputPayload)->PackageSparingTrigger = !ClearStatus; for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = FwCmdGetPackageSparingPolicy(pDimms[Index], &pPayloadPackageSparingPolicy); if (EFI_ERROR(ReturnCode)) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED); ReturnCode = EFI_DEVICE_ERROR; continue; } /* If the package sparing policy has been enabled and executed, Support Bit will be 0x00 but the Enable Bit will still be 0x01 to indicate that the dimm has PackageSparing Policy Enabled before. */ if (pDimms[Index]->SkuInformation.PackageSparingCapable && pPayloadPackageSparingPolicy->Enable && ((!ClearStatus && pPayloadPackageSparingPolicy->Supported) || (ClearStatus && !pPayloadPackageSparingPolicy->Supported))) { //Inject the error if PackageSparing policy is available and is supported ReturnCode = FwCmdInjectError(pDimms[Index], SubopSoftwareErrorTriggers, (VOID *)pInputPayload, &FwStatus); if (EFI_ERROR(ReturnCode)) { if (FwStatus == FW_INJECTION_NOT_ENABLED) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_ERROR_INJECTION_BIOS_KNOB_NOT_ENABLED); continue; } SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED); ReturnCode = EFI_DEVICE_ERROR; FREE_POOL_SAFE(pPayloadPackageSparingPolicy); continue; } } else { ReturnCode = EFI_UNSUPPORTED; SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_NOT_SUPPORTED); FREE_POOL_SAFE(pPayloadPackageSparingPolicy); continue; } SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_SUCCESS); FREE_POOL_SAFE(pPayloadPackageSparingPolicy); } FREE_POOL_SAFE(pPayloadPackageSparingPolicy); break; case ERROR_INJ_DIRTY_SHUTDOWN: pInputPayload = AllocateZeroPool(sizeof(PT_INPUT_PAYLOAD_INJECT_SW_TRIGGERS)); if (NULL == pInputPayload) { ReturnCode = EFI_OUT_OF_RESOURCES; SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_NO_MEM); goto Finish; } ((PT_INPUT_PAYLOAD_INJECT_SW_TRIGGERS *)pInputPayload)->TriggersToModify = DIRTY_SHUTDOWN_TRIGGER; ((PT_INPUT_PAYLOAD_INJECT_SW_TRIGGERS *)pInputPayload)->DirtyShutdownTrigger = !ClearStatus; for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = FwCmdInjectError(pDimms[Index], SubopSoftwareErrorTriggers, pInputPayload, &FwStatus); if (EFI_ERROR(ReturnCode)) { if (FwStatus == FW_INJECTION_NOT_ENABLED) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_ERROR_INJECTION_BIOS_KNOB_NOT_ENABLED); continue; } SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED); ReturnCode = EFI_DEVICE_ERROR; continue; } SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_SUCCESS); } break; case ERROR_INJ_FATAL_MEDIA_ERR: pInputPayload = AllocateZeroPool(sizeof(PT_INPUT_PAYLOAD_INJECT_SW_TRIGGERS)); if (pInputPayload == NULL) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_NO_MEM); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ((PT_INPUT_PAYLOAD_INJECT_SW_TRIGGERS *)pInputPayload)->TriggersToModify = FATAL_ERROR_TRIGGER; ((PT_INPUT_PAYLOAD_INJECT_SW_TRIGGERS *)pInputPayload)->FatalErrorTrigger = !ClearStatus; for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = FwCmdInjectError(pDimms[Index], SubopSoftwareErrorTriggers, (VOID *) pInputPayload, &FwStatus); if (EFI_ERROR(ReturnCode)) { if (FwStatus == FW_INJECTION_NOT_ENABLED) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_ERROR_INJECTION_BIOS_KNOB_NOT_ENABLED); continue; } SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED); ReturnCode = EFI_DEVICE_ERROR; continue; } if (ClearStatus == 1) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_SUCCESS_REQUIRES_POWER_CYCLE); } else { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_SUCCESS); } } break; case ERROR_INJ_POISON: pInputPayload = AllocateZeroPool(sizeof(PT_INPUT_PAYLOAD_INJECT_POISON)); if (pInputPayload == NULL) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_NO_MEM); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } if (pPoisonType == NULL) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_PARAMETER); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ((PT_INPUT_PAYLOAD_INJECT_POISON *)pInputPayload)->Memory = *pPoisonType; if (pInjectPoisonAddress == NULL) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_PARAMETER); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ((PT_INPUT_PAYLOAD_INJECT_POISON *)pInputPayload)->DpaAddress = *pInjectPoisonAddress; ((PT_INPUT_PAYLOAD_INJECT_POISON *)pInputPayload)->Enable = !ClearStatus; for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = GetDimmSecurityState(pDimms[Index], PT_TIMEOUT_INTERVAL, &SecurityState); if (EFI_ERROR(ReturnCode)) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_UNABLE_TO_GET_SECURITY_STATE); continue; } if (SecurityState & SECURITY_MASK_LOCKED) { NVDIMM_DBG("Invalid security check- poison inject error cannot be applied"); SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_UNABLE_TO_GET_SECURITY_STATE); ReturnCode = EFI_INVALID_PARAMETER; continue; } ReturnCode = FwCmdGetARS(pDimms[Index], &DimmARSStatus); if (LONG_OP_STATUS_IN_PROGRESS == DimmARSStatus) { NVDIMM_ERR("ARS in progress.\n"); SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_ARS_IN_PROGRESS); ReturnCode = EFI_DEVICE_ERROR; goto Finish; } ReturnCode = FwCmdInjectError(pDimms[Index], SubopErrorPoison, (VOID *) pInputPayload, &FwStatus); if (EFI_ERROR(ReturnCode)) { if (FwStatus == FW_INJECTION_NOT_ENABLED) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_ERROR_INJECTION_BIOS_KNOB_NOT_ENABLED); continue; } SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED); ReturnCode = EFI_DEVICE_ERROR; continue; } SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_SUCCESS); } break; case ERROR_INJ_PERCENTAGE_REMAINING: pInputPayload = AllocateZeroPool(sizeof(PT_INPUT_PAYLOAD_INJECT_SW_TRIGGERS)); if (pInputPayload == NULL) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_NO_MEM); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } if (pPercentageRemaining == NULL) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_INVALID_PARAMETER); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ((PT_INPUT_PAYLOAD_INJECT_SW_TRIGGERS *)pInputPayload)->TriggersToModify = SPARE_BLOCK_PERCENTAGE_TRIGGER; ((PT_INPUT_PAYLOAD_INJECT_SW_TRIGGERS *)pInputPayload)->SpareBlockPercentageTrigger.Separated.Enable = !ClearStatus; ((PT_INPUT_PAYLOAD_INJECT_SW_TRIGGERS *)pInputPayload)->SpareBlockPercentageTrigger.Separated.Value = *pPercentageRemaining; for (Index = 0; Index < DimmsNum; Index++) { ReturnCode = FwCmdInjectError(pDimms[Index], SubopSoftwareErrorTriggers, (VOID *)pInputPayload, &FwStatus); if (EFI_ERROR(ReturnCode)) { if (FwStatus == FW_INJECTION_NOT_ENABLED) { SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_ERROR_INJECTION_BIOS_KNOB_NOT_ENABLED); continue; } SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_ERR_OPERATION_FAILED); ReturnCode = EFI_DEVICE_ERROR; continue; } SetObjStatusForDimm(pCommandStatus, pDimms[Index], NVM_SUCCESS); } } // Repopulate boot status register & bitmask values for all targeted DIMMs whenever FwCmdInjectError has been attempted for (Index = 0; Index < DimmsNum; Index++) { CHECK_RESULT(GetBSRAndBootStatusBitMask(pThis, pDimms[Index]->DimmID, &pDimms[Index]->Bsr.AsUint64, &pDimms[Index]->BootStatusBitmask), Finish); } Finish: FREE_POOL_SAFE(pInputPayload); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** GetBsr value and return bsr or boot status bitmask depending on the requested options UEFI - Read directly from BSR register OS - Get BSR value from BIOS emulated command @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmID PMem module handle of the PMem module @param[out] pBsrValue pointer to BSR register value OPTIONAL @param[out] pBootStatusBitMask pointer to BootStatusBitmask OPTIONAL @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_NO_RESPONSE BSR value returned by FW is invalid @retval EFI_SUCCESS Success @retval Other errors failure of FW commands **/ EFI_STATUS EFIAPI GetBSRAndBootStatusBitMask( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT UINT64 *pBsrValue OPTIONAL, OUT UINT16 *pBootStatusBitmask OPTIONAL ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; COMMAND_STATUS *pCommandStatus = NULL; UINT64 *pLocalBsr = NULL; UINT16 *pLocalBootStatusBitmask = NULL; DIMM_BSR JunkBsr; // passed to function with value never used UINT16 JunkBootStatusBitmask = 0; // passed to function with value never used NVDIMM_ENTRY(); if (pThis == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ZeroMem(&JunkBsr, sizeof(JunkBsr)); pLocalBsr = pBsrValue; if (pLocalBsr == NULL) { pLocalBsr = (UINT64*)&JunkBsr; } pLocalBootStatusBitmask = pBootStatusBitmask; if (pLocalBootStatusBitmask == NULL) { pLocalBootStatusBitmask = &JunkBootStatusBitmask; } // Set BSR and BootStatusBitmask to default values *pLocalBsr = 0; *pLocalBootStatusBitmask = DIMM_BOOT_STATUS_UNKNOWN; // Initialize pCommandStatus and throw away eventually because API // doesn't provide it and it is required for VerifyTargetDimms() CHECK_RESULT(InitializeCommandStatus(&pCommandStatus), Finish); CHECK_RESULT(VerifyTargetDimms(&DimmID, 1, NULL, 0, REQUIRE_DCPMMS_SELECT_ALL, pDimms, &DimmsNum, pCommandStatus), Finish); // Populate boot status bitmask based on DDRT/SMBUS interface status CHECK_RESULT(PopulateDimmBootStatusBitmaskInterfaceBits(pDimms[0], pLocalBootStatusBitmask), Finish); CHECK_RESULT(FwCmdGetBsr(pDimms[0], pLocalBsr), Finish); // Populate boot status bitmask based on DIMM BSR value CHECK_RESULT(PopulateDimmBootStatusBitmaskBsrBits(pDimms[0], (DIMM_BSR *)pLocalBsr, pLocalBootStatusBitmask), Finish); Finish: FreeCommandStatus(&pCommandStatus); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get Command Access Policy is used to retrieve a list of FW commands that may be restricted. Passing pCapInfo as NULL will provide the maximum number of possible return elements by updating pCount. @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmID Handle of the DIMM @param[in,out] pCount IN: Count is number of elements in the pCapInfo array. OUT: number of elements written to pCapInfo @param[out] pCapInfo Array of Command Access Policy Entries. If NULL, pCount will be updated with maximum number of elements possible. OPTIONAL @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetCommandAccessPolicy( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, IN OUT UINT32 *pCount, OUT COMMAND_ACCESS_POLICY_ENTRY *pCapInfo OPTIONAL ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimm = NULL; UINT32 Index = 0; COMMAND_ACCESS_POLICY_ENTRY *pCapEntries; //The following CAP entries apply up to and including FIS 2.0 COMMAND_ACCESS_POLICY_ENTRY CapEntriesOrig[] = { { PtSetSecInfo, SubopOverwriteDimm, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID}, { PtSetSecInfo, SubopSetMasterPass, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetSecInfo, SubopSetPass, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetSecInfo, SubopSecFreezeLock, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetFeatures, SubopAlarmThresholds, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetFeatures, SubopConfigDataPolicy, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetFeatures, SubopAddressRangeScrub, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetAdminFeatures, SubopPlatformDataInfo, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetAdminFeatures, SubopLatchSystemShutdownState, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtUpdateFw, SubopUpdateFw, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID } }; //The following CAP entries apply to FIS 2.1-2.2 COMMAND_ACCESS_POLICY_ENTRY CapEntries_2_1[] = { { PtSetSecInfo, SubopOverwriteDimm, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID}, { PtSetSecInfo, SubopSetMasterPass, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetSecInfo, SubopSetPass, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetSecInfo, SubopSecEraseUnit, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetSecInfo, SubopSecFreezeLock, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetFeatures, SubopAlarmThresholds, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetFeatures, SubopConfigDataPolicy, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetFeatures, SubopAddressRangeScrub, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetAdminFeatures, SubopPlatformDataInfo, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetAdminFeatures, SubopLatchSystemShutdownState, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtUpdateFw, SubopUpdateFw, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID } }; //The following CAP entries apply to FIS 2.3+ COMMAND_ACCESS_POLICY_ENTRY CapEntries_2_3[] = { { PtSetSecInfo, SubopOverwriteDimm, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID}, { PtSetSecInfo, SubopSetMasterPass, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetSecInfo, SubopSetPass, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetSecInfo, SubopSecEraseUnit, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetSecInfo, SubopSecFreezeLock, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetFeatures, SubopAlarmThresholds, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetFeatures, SubopConfigDataPolicy, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetFeatures, SubopAddressRangeScrub, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetAdminFeatures, SubopPlatformDataInfo, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtSetAdminFeatures, SubopLatchSystemShutdownState, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtUpdateFw, SubopUpdateFw, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID }, { PtUpdateFw, SubopFwActivate, COMMAND_ACCESS_POLICY_RESTRICTION_INVALID } }; NVDIMM_ENTRY(); if (pThis == NULL || pCount == NULL) { NVDIMM_DBG("One or more parameters are NULL"); goto Finish; } pDimm = GetDimmByPid(DimmID, &gNvmDimmData->PMEMDev.Dimms); if (pDimm == NULL || !IsDimmManageable(pDimm)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (NULL == pCapInfo) { // pCapInfo is NULL so just getting size if (((pDimm->FwVer.FwApiMajor == 0x2) && (pDimm->FwVer.FwApiMinor >= 0x3)) || (pDimm->FwVer.FwApiMajor >= 0x3)) { *pCount = COUNT_OF(CapEntries_2_3); ReturnCode = EFI_SUCCESS; } else if ((pDimm->FwVer.FwApiMajor == 0x2) && (pDimm->FwVer.FwApiMinor >= 0x1)) { *pCount = COUNT_OF(CapEntries_2_1); ReturnCode = EFI_SUCCESS; } else { *pCount = COUNT_OF(CapEntriesOrig); ReturnCode = EFI_SUCCESS; } NVDIMM_DBG("Setting pCount to %d.", *pCount); goto Finish; } if (((pDimm->FwVer.FwApiMajor == 0x2) && (pDimm->FwVer.FwApiMinor >= 0x3)) || (pDimm->FwVer.FwApiMajor >= 0x3)) { if (*pCount == COUNT_OF(CapEntries_2_3)) { pCapEntries = CapEntries_2_3; } else { NVDIMM_DBG("Parameter pCount should be %d for FIS2.3+ DIMM. Received pCount = %d.", COUNT_OF(CapEntries_2_3), *pCount); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } else if ((pDimm->FwVer.FwApiMajor == 0x2) && (pDimm->FwVer.FwApiMinor >= 0x1)) { if (*pCount == COUNT_OF(CapEntries_2_1)) { pCapEntries = CapEntries_2_1; } else { NVDIMM_DBG("Parameter pCount should be %d for FIS2.1-2 DIMM. Received pCount = %d.", COUNT_OF(CapEntries_2_1), *pCount); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } else { if (*pCount == COUNT_OF(CapEntriesOrig)) { pCapEntries = CapEntriesOrig; } else { NVDIMM_DBG("Parameter pCount should be %d for pre-FIS2.1 DIMM. Received pCount = %d.", COUNT_OF(CapEntriesOrig), *pCount); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } } for (Index = 0; Index < *pCount; Index++) { ReturnCode = FwCmdGetCommandAccessPolicy(pDimm, pCapEntries[Index].Opcode, pCapEntries[Index].SubOpcode, &pCapEntries[Index].Restriction); if (EFI_UNSUPPORTED == ReturnCode) { pCapEntries[Index].Restriction = COMMAND_ACCESS_POLICY_RESTRICTION_UNSUPPORTED; } else if (EFI_ERROR(ReturnCode)) { // If error, leave entry as invalid, but make sure it's still set pCapEntries[Index].Restriction = COMMAND_ACCESS_POLICY_RESTRICTION_INVALID; } CopyMem_S(&pCapInfo[Index], (sizeof(*pCapInfo)), &pCapEntries[Index], (sizeof(*pCapInfo))); NVDIMM_DBG("Retrieved Command Access Policy for 0x%x:0x%x. ReturnCode=0x%x.", pCapEntries[Index].Opcode, pCapEntries[Index].SubOpcode, ReturnCode); } ReturnCode = EFI_SUCCESS; goto Finish; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get Command Effect Log is used to retrieve a list of FW commands and their effects on the PMem module subsystem. @param[in] pThis - A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmID - Handle of the PMem module @param[out] ppLogEntry - A pointer to the CEL entry table for a given PMem module. @param[out] pEntryCount - The number of CEL entries @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetCommandEffectLog( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT COMMAND_EFFECT_LOG_ENTRY **ppLogEntry, OUT UINT32 *pEntryCount ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; COMMAND_STATUS *pCommandStatus = NULL; if (pThis == NULL) { NVDIMM_DBG("One or more parameters are NULL"); goto Finish; } // Make a dummy command status for now for VerifyTargetDimms. Hopefully // we can correct the ShowCEL command and this api call to pass it in and // use it CHECK_RESULT(InitializeCommandStatus(&pCommandStatus), Finish); CHECK_RESULT(VerifyTargetDimms(&DimmID, 1, NULL, 0, REQUIRE_DCPMMS_MANAGEABLE, pDimms, &DimmsNum, pCommandStatus), Finish); CHECK_RESULT(FwCmdGetCommandEffectLog(pDimms[0], ppLogEntry, pEntryCount), Finish); Finish: FreeCommandStatus(&pCommandStatus); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #ifndef OS_BUILD /** This function makes calls to the dimms required to initialize the driver. @retval EFI_SUCCESS if no errors. @retval EFI_xxxx depending on error encountered. **/ EFI_STATUS LoadArsList() { UINT32 x = 0; UINT32 records = 0; EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); if (gArsBadRecordsCount >= 0) { NVDIMM_DBG("ARS list already loaded.\n"); goto Finish; } gArsBadRecordsCount = 0; //First check how many records exist by passing NULL ReturnCode = gNvmDimmData->pDcpmmProtocol->DcpmmArsStatus(&records, NULL); if (ReturnCode == EFI_NOT_READY) { NVDIMM_WARN("BIOS reports not ready for full ARS list. The returned list may be partial."); ReturnCode = EFI_SUCCESS; } if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Could not obtain the ARS bad address list count"); goto Finish; } //if there are records, allocate the space for them and obtain them if (records > 0) { gArsBadRecords = (DCPMM_ARS_ERROR_RECORD *)AllocateZeroPool(sizeof(DCPMM_ARS_ERROR_RECORD) * records); if (gArsBadRecords == NULL) { NVDIMM_WARN("Failed to allocate memory for the bad ARS records"); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } ReturnCode = gNvmDimmData->pDcpmmProtocol->DcpmmArsStatus(&records, gArsBadRecords); if (ReturnCode == EFI_NOT_READY) { NVDIMM_WARN("BIOS reports not ready for full ARS list. The returned list may be partial."); ReturnCode = EFI_SUCCESS; } if (EFI_ERROR(ReturnCode)) { NVDIMM_WARN("Could not obtain the ARS bad address list"); FreePool(gArsBadRecords); goto Finish; } gArsBadRecordsCount = records; for (; x < records; x++) { NVDIMM_DBG("ArsBadRecords[%d] = 0x%llx, len = 0x%llx, nfit handle = 0x%llx", x, gArsBadRecords[x].SpaOfErrLoc, gArsBadRecords[x].Length, gArsBadRecords[x].NfitHandle); } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif /** Gets value of Smbus protocol configuration global variable from platform @param[in] pThis A pointer to EFI DCPMM CONFIG PROTOCOL structure @param[in,out] pAttribs A pointer to a variable used to store protocol and payload settings @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid FW Command Parameter. **/ EFI_STATUS EFIAPI GetFisTransportAttributes( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS *pAttribs ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (NULL == pThis || NULL == pAttribs) { NVDIMM_DBG("Input parameter is NULL"); goto Finish; } pAttribs->Protocol = gTransportAttribs.Protocol; pAttribs->PayloadSize = gTransportAttribs.PayloadSize; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Sets value of Smbus protocol configuration global variable for platform @param[in] pThis A pointer to EFI DCPMM CONFIG PROTOCOL structure @param[in] Attribs A pointer to a variable used to store protocol and payload settings @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid FW Command Parameter. **/ EFI_STATUS EFIAPI SetFisTransportAttributes( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS Attribs ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (NULL == pThis) { NVDIMM_DBG("Input parameter is NULL"); goto Finish; } if ((Attribs.Protocol > FisTransportAuto) || (Attribs.PayloadSize > FisTransportSizeAuto)) { // Covers case where multiple values are somehow set NVDIMM_DBG("Incorrect transport attributes detected"); goto Finish; } gTransportAttribs.Protocol = Attribs.Protocol; gTransportAttribs.PayloadSize = Attribs.PayloadSize; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get minimal FWImageMaxSize value for all designated DIMMs @param[in] pNvmDimmConfigProtocol - The open config protocol @param[in] pDimms - Pointer to an array of DIMMs @param[in] DimmsNum - Number of items in array of DIMMs @retval The minimal allowed size of firmware image buffer in bytes @retval MAX_UINT64 is an error **/ UINT64 GetMinFWImageMaxSize( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN DIMM *pDimms[MAX_DIMMS], IN UINT32 DimmsNum ) { UINT64 RetVal = 0; UINT64 MinSize = MAX_UINT64; EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM_INFO *pDimm = NULL; UINT32 Index = 0; NVDIMM_ENTRY(); if (NULL == pNvmDimmConfigProtocol || NULL == pDimms) { NVDIMM_DBG("One or more parameters are NULL"); goto Finish; } if (0 == DimmsNum) { NVDIMM_DBG("Invalid DIMMs number passed"); goto Finish; } pDimm = AllocateZeroPool(sizeof(*pDimm)); if (pDimm == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } for (Index = 0; Index < DimmsNum; Index++) { CHECK_RESULT((GetDimmInfo(pDimms[Index], DIMM_INFO_CATEGORY_FW_IMAGE_INFO, pDimm)), Finish); if (pDimm->FWImageMaxSize > 0 && pDimm->FWImageMaxSize < MinSize) { MinSize = pDimm->FWImageMaxSize; } } RetVal = MinSize; Finish: FREE_POOL_SAFE(pDimm); NVDIMM_EXIT_I64(ReturnCode); return RetVal; } #ifndef OS_BUILD /** Gets value of PcdDebugPrintErrorLevel for the pmem driver @param[in] pThis A pointer to EFI DCPMM CONFIG PROTOCOL structure @param[out] ErrorLevel A pointer used to store the value of debug print error level @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid ErrorLevel Parameter. **/ EFI_STATUS EFIAPI GetDriverDebugPrintErrorLevel( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pErrorLevel ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (NULL == pThis || NULL == pErrorLevel) { NVDIMM_DBG("Input parameter is NULL"); goto Finish; } *pErrorLevel = PatchPcdGet32(PcdDebugPrintErrorLevel); ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Sets value of PcdDebugPrintErrorLevel for the pmem driver @param[in] pThis A pointer to EFI DCPMM CONFIG PROTOCOL structure @param[in] ErrorLevel The new value to assign to debug print error level @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid ErrorLevel Parameter. **/ EFI_STATUS EFIAPI SetDriverDebugPrintErrorLevel( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT32 ErrorLevel ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_ENTRY(); if (NULL == pThis) { NVDIMM_DBG("Input parameter is NULL"); goto Finish; } PatchPcdSet32(PcdDebugPrintErrorLevel, ErrorLevel); ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif //OS_BUILD /** Get FIPS Mode retrieves the current FIPS Mode for the DimmID provided. @param[in] pThis - A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] DimmID - Handle of the PMem module @param[out] pFIPSMode - A pointer to a FIPS_MODE struct to fill in @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetFIPSMode( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT FIPS_MODE *pFIPSMode, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; DIMM *pDimms[MAX_DIMMS]; UINT32 DimmsNum = 0; CHECK_RESULT(VerifyTargetDimms(&DimmID, 1, NULL, 0, REQUIRE_DCPMMS_MANAGEABLE, pDimms, &DimmsNum, pCommandStatus), Finish); CHECK_RESULT(FwCmdSmallPayload(pDimms[0], PtGetSecInfo, SubOpGetFIPSMode, NULL, 0, (UINT8 *)pFIPSMode, sizeof(*pFIPSMode)), Finish); Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; }ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Driver/NvmDimmConfig.h000066400000000000000000001723371440615110200243520ustar00rootroot00000000000000/* * Copyright (c) 2015-2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file NvmDimmConfig.h * @brief Implementation of the EFI_DCPMM_CONFIG2_PROTOCOL, a custom protocol * to configure and manage PMem modules **/ /** * @mainpage Intel® Optane™ PMem Software UEFI FW Protocols * * @section Introduction * This document provides descriptions of protocols implemented by the * Intel® Optane™ PMem Software Unified Extensible Firmware Interface (UEFI) Firmware (FW). * Protocols implemented include: * - EFI_DRIVER_BINDING_PROTOCOL * - EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL * - EFI_DRIVER_DIAGNOSTICS_PROTOCOL and EFI_DRIVER_DIAGNOSTICS2_PROTOCOL * - Provides diagnostic tests for the specified PMem module * - EFI_DRIVER_HEALTH_PROTOCOL * - Provides standardized health status for the specified PMem module * - EFI_DCPMM_CONFIG2_PROTOCOL * - Provides configuration management for the specified PMem module, including: * - Discovery * - Provisioning * - Health & Instrumentation * - Support and Maintenance * - Diagnostics & Debug * - EFI_FIRMWARE_MANAGEMENT_PROTOCOL * - Provides standardized PMem module firmware management * - EFI_STORAGE_SECURITY_COMMAND_PROTOCOL * - Provides standardized PMem module security functionality * - EFI_BLOCK_IO_PROTOCOL * - Provides BLOCK IO access to the specified PMem module namespaces * - EFI_NVDIMM_LABEL_PROTOCOL * - Provides standardized access to the specified PMem module labels * - Automated Provisioning flow using an EFI_VARIABLE * * @section AutoProvisioning Automated Provisioning * Automated Provisioning provides a mechanism to provision both persistent * memory regions and namespaces on the next boot by accessing an exposed * EFI_VARIABLE. This mechanism may be particularly useful to initiate provisioning * via an out-of-band (OOB) path, like a Baseboard Management Controller (BMC). * * The UEFI driver will determine if mode provisioning is required by first checking * the UEFI variable status field and then checking the Platform Config Data (PCD) data stored on the PMem module * to ensure it matches (if necessary). * * The UEFI driver will determine if namespace provisioning is required by first * checking the UEFI variable status field and then checking for empty interleave * sets. * * The UEFI_VARIABLE IntelDIMMConfig is described next. \n \n * * Globally Unique Identifier (GUID): {76fcbfb6-38fe-41fd-901d-16453122f035} \n \n * * Attributes: EFI_VARIABLE_NON_VOLATILE, EFI_VARIABLE_BOOTSERVICE_ACCESS \n * * Variable Name | Size(bytes) | Data * ------------------------- | ------------ | ----------------------------------- * Revision | 1 | 1 (Read only written by driver) * ProvisionCapacityMode | 1 | 0: Manual - PMem module capacity provisioning via user interface. (Default).
1: Auto - Automatically provision all PMem module capacity during system boot if this request does not match the current PCD metadata stored on the PMem modules.
Note: Auto provisioning may result is loss of persistent data stored on the PMem modules. * MemorySize | 1 | If ProvisionCapacityMode = Auto, the % of the total capacity to provision in Memory Mode (0-100%). 0: (Default) * PMType | 1 | If ProvisionCapacityMode = Auto, the type of persistent memory to provision (if not 100% Memory Mode).
0: App Direct
1: App Direct, Not Interleaved * ProvisionNamespaceMode | 1 | 0: Manual - Namespace provisioning via user interface. (Default).
1: Auto - Automatically create a namespace on all PMem module App Direct interleave sets is one does not exist already. * NamespaceFlags | 1 | If ProvisionNamespaceMode=Auto, the flags to apply when automatically creating namespaces.
0: None (Default).
1: BTT * ProvisionCapacityStatus | 1 | 0: Unknown - Check PCD if ProvisionCapacityMode = Auto (Default).
1: Successfully provisioned.
2: Error.
3: Pending reset. * ProvisionNamespaceStatus | 1 | 0: Unknown - Check LSA if ProvisionNamespaceMode = Auto (Default).
1: Successfully created namespaces.
2: Error. * NamespaceLabelVersion | 1 | Namespace label version to initialize when provision capacity.
0: Latest Version (Currently 1.2).
1: 1.1.
2: 1.2 * Reserved | 7 | Reserved. * * Figure 'Auto Provisioning Flow Diagram' describes the decision tree implemented. * * \image latex AutoProvisioningFlowDiagram.png "Auto Provisioning Flow Diagram" * */ #ifndef _NVMDIMM_CONFIG_H_ #define _NVMDIMM_CONFIG_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "NvmDimmConfigInt.h" extern EFI_GUID gNvmDimmConfigProtocolGuid; extern EFI_GUID gNvmDimmPbrProtocolGuid; #define DEVICE_LOCATOR_LEN 128 //!< DIMM Device Locator buffer length #define FEATURE_NOT_SUPPORTED 0 #define FEATURE_SUPPORTED 1 #define EFI_PERSISTENT_MEMORY_REGION 14 #define MSR_RAPL_POWER_UNIT 0x618 #define FW_UPDATE_INIT_TRANSFER 0x0 #define FW_UPDATE_CONTINUE_TRANSFER 0x1 #define FW_UPDATE_END_TRANSFER 0x2 #define FW_UPDATE_LARGE_PAYLOAD_SELECTOR 0x0 #define FW_UPDATE_SMALL_PAYLOAD_SELECTOR 0x1 #define NFIT_PLATFORM_CAPABILITIES_BIT0 0x1 #define NFIT_MEMORY_CONTROLLER_FLUSH_BIT1 (NFIT_PLATFORM_CAPABILITIES_BIT0 << 0x1) /** The update goes in 3 steps: initialization, data, end, where the data step can be done many times. Each of those steps must be done at least one, so the minimum number of packets will be 3. **/ #define FW_UPDATE_SP_MINIMUM_PACKETS 3 #define FW_UPDATE_SP_MAXIMUM_PACKETS MAX_FIRMWARE_IMAGE_SIZE_B / UPDATE_FIRMWARE_SMALL_PAYLOAD_DATA_PACKET_SIZE #pragma pack(push) #pragma pack(1) typedef struct { UINT16 TransactionType : 2; UINT16 PacketNumber : 14; UINT8 PayloadTypeSelector; UINT8 Reserved; UINT8 Data[UPDATE_FIRMWARE_SMALL_PAYLOAD_DATA_PACKET_SIZE]; UINT8 Reserved1[60]; } FW_SMALL_PAYLOAD_UPDATE_PACKET; #pragma pack(pop) /** SKU types & capabilities **/ typedef enum { /** SKU Capabilities **/ SkuMemoryModeOnly, SkuAppDirectModeOnly, DimmSkuType_Reserved, SkuTriMode, SkuPackageSparingCapable, /** SKU Types **/ SkuSoftProgrammableSku, SkuStandardSecuritySku, SkuControlledCountrySku } DimmSkuType; /** Retrieve the number of PMem modules in the system found in NFIT @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pDimmCount The number of PMem modules found in NFIT. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetDimmCount( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pDimmCount ); /** Retrieve the number of uninitialized PMem modules in the system found through SMBUS @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pDimmCount The number of PMem modules found through SMBUS. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetUninitializedDimmCount( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pDimmCount ); /** Retrieve the list of PMem modules found in NFIT @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmCount The size of pDimms. @param[in] DimmInfoCategories See @ref DIMM_INFO_CATEGORY_TYPES specifies which (if any) additional FW api calls is desired. If ::DIMM_INFO_CATEGORY_NONE, then only the properties from the pDimms struct(s) will be populated. @param[out] pDimms The PMem module list found in NFIT. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetDimms( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT32 DimmCount, IN DIMM_INFO_CATEGORIES DimmInfoCategories, OUT DIMM_INFO *pDimms ); /** Retrieve the list of non-functional PMem modules found in NFIT Note: To properly fill in these fields, it is necessary to call GetDimm() after this with your desired DIMM_INFO_CATEGORIES. @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmCount The size of pDimms. @param[out] pDimms The PMem module list @retval EFI_SUCCESS The module list was returned properly @retval EFI_INVALID_PARAMETER one or more parameter are NULL or invalid. @retval EFI_NOT_FOUND PMem module not found **/ EFI_STATUS EFIAPI GetUninitializedDimms( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT32 DimmCount, OUT DIMM_INFO *pDimms ); /** Retrieve the details about the PMem module specified with pid found in NFIT @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Pid The ID of the PMem module to retrieve @param[in] DimmInfoCategories @ref DIMM_INFO_CATEGORY_TYPES specifies which (if any) additional FW api calls is desired. If ::DIMM_INFO_CATEGORY_NONE, then only the properties from the pDimm struct will be populated. @param[out] pDimmInfo A pointer to the PMem module found in NFIT @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetDimm( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 Pid, IN DIMM_INFO_CATEGORIES DimmInfoCategories, OUT DIMM_INFO *pDimmInfo ); #ifdef OS_BUILD /** Retrieve the PMON register values from the PMem module @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Pid The ID of the PMem module to retrieve @param[in] SmartDataMask This will specify whether or not to return the extra smart data along with the PMON Counter data @param[out] pPayloadPMONRegisters A pointer to the output payload PMON registers @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetPMONRegisters( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 Pid, IN UINT8 SmartDataMask, OUT PMON_REGISTERS *pPayloadPMONRegisters ); /** Set the PMON register values from the PMem module @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Pid The ID of the PMem module to retrieve @param[in] PMONGroupEnable Specifies which PMON Group to enable @param[out] pPayloadPMONRegisters A pointer to the output payload PMON registers @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI SetPMONRegisters( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 Pid, IN UINT8 PMONGroupEnable ); #endif /** Retrieve the list of sockets (physical processors) in the host server @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pSocketCount The size of the list of sockets. @param[out] ppSockets Pointer to the list of sockets. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetSockets( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pSocketCount, OUT SOCKET_INFO **ppSockets ); /* Retrieve an System Management BIOS (SMBIOS) table type 17 or type 20 for a specific PMem module. Function available in the DEBUG build only! @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Pid The ID of the PMem module to retrieve @param[in] Type The Type of SMBIOS table to retrieve. Valid values: 17, 20. @param[out] pTable A pointer to the SMBIOS table @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetDimmSmbiosTable( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 Pid, IN UINT8 Type, OUT SMBIOS_STRUCTURE_POINTER *pTable ); /** Check NVM device security state Function checks security state of a set of PMem modules. It sets security state to mixed when not all PMem modules have the same state. @param[in] pThis a pointer to EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[out] pSecurityState security state of a PMem module or all PMem modules @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetSecurityState( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, OUT UINT8 *pSecurityState, OUT COMMAND_STATUS *pCommandStatus ); /** Set security state on multiple PMem modules. If there is a failure on one of the PMem modules, the function does not continue onto the remaining modules but exits with an error. @param[in] pThis a pointer to EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] pDimmIds Pointer to an array of DIMM IDs - if NULL, execute operation on all dimms @param[in] DimmIdsCount Number of items in array of DIMM IDs @param[in] SecurityOperation Security Operation code @param[in] pPassphrase a pointer to string with current passphrase. For default Master Passphrase (0's) use a zero length, null terminated string. @param[in] pNewPassphrase a pointer to string with new passphrase @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER when pLockState is NULL @retval EFI_OUT_OF_RESOURCES couldn't allocate memory for a structure @retval EFI_UNSUPPORTED LockState to be set is not recognized, or mixed sku of DCPMMs detected @retval EFI_DEVICE_ERROR setting state for a DIMM failed @retval EFI_NOT_FOUND a DIMM was not found @retval EFI_NO_RESPONSE FW busy for one or more dimms @retval EFI_SUCCESS security state correctly set **/ EFI_STATUS EFIAPI SetSecurityState( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 SecurityOperation, IN CHAR16 *pPassphrase, IN CHAR16 *pNewPassphrase, OUT COMMAND_STATUS *pCommandStatus ); /** Retrieve the NFIT ACPI table @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] ppNFit A pointer to the output NFIT table @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetAcpiNFit ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT ParsedFitHeader **ppNFit ); /** Retrieve the PCAT ACPI table @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] ppPcat output buffer with PCAT tables @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetAcpiPcat ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT ParsedPcatHeader **ppPcat ); /** Retrieve the PMTT ACPI table @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] ppPMTT output buffer with PMTT tables. This buffer must be freed by caller. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetAcpiPMTT( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT VOID **ppPMTT ); /** Get Platform Config Data The caller is responsible for freeing ppDimmPcdInfo by using FreeDimmPcdInfoArray. @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] PcdTarget Target PCD partition: ALL=0, CONFIG=1, NAMESPACES=2 @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[out] ppDimmPcdInfo Pointer to output array of PCDs @param[out] pDimmPcdInfoCount Number of items in PMem module PCD Info @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetPcd( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT8 PcdTarget, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, OUT DIMM_PCD_INFO **ppDimmPcdInfo, OUT UINT32 *pDimmPcdInfoCount, OUT COMMAND_STATUS *pCommandStatus ); /** Modifies select partition data from the PCD @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] ConfigIdMask Bitmask that defines which config to delete. See @ref DELETE_PCD_CONFIG_ALL_MASK @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more input parameters are NULL @retval EFI_NO_RESPONSE FW busy for one or more PMem modules @retval EFI_OUT_OF_RESOURCES Memory allocation failure **/ EFI_STATUS EFIAPI ModifyPcdConfig( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT32 ConfigIdMask, OUT COMMAND_STATUS *pCommandStatus ); /** Flash new SPI image to a specified PMem module @param[in] DimmPid PMem module ID of a PMem module on which recovery is to be performed @param[in] pNewSpiImageBuffer is a pointer to new SPI FW image @param[in] ImageBufferSize is SPI image size in bytes @param[out] pNvmStatus NVM error code @param[out] pCommandStatus command status list @retval EFI_INVALID_PARAMETER One of parameters provided is not acceptable @retval EFI_NOT_FOUND there is no PMem module with such Pid @retval EFI_DEVICE_ERROR Unable to communicate with PMem module SPI @retval EFI_OUT_OF_RESOURCES Unable to allocate memory for a data structure @retval EFI_ACCESS_DENIED When SPI access is not unlocked @retval EFI_SUCCESS Update has completed successfully **/ EFI_STATUS EFIAPI RecoverDimmFw( IN UINT32 DimmHandle, IN CONST VOID *pNewSpiImageBuffer, IN UINT64 ImageBufferSize, IN CHAR16 *pWorkingDirectory OPTIONAL, OUT NVM_STATUS *pNvmStatus, OUT COMMAND_STATUS *pCommandStatus ); /** Update firmware or training data in one or all PMem modules of the system @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds is a pointer to an array of PMem module IDs - if NULL, execute operation on all PMem modules @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] pFileName Name is a pointer to a file containing FW image @param[in] pWorkingDirectory is a pointer to a path to FW image file @param[in] Examine flag enables image verification only @param[in] Force flag suppresses warning message in case of attempted downgrade @param[in] Recovery flag determine that recovery update should be performed @param[in] Reserved Set to FALSE @param[out] pFwImageInfo is a pointer to a structure containing FW image information need to be provided if examine flag is set @param[out] pCommandStatus Structure containing detailed NVM error codes @remarks If Address Range Scrub (ARS) is in progress on any target PMem module, an attempt will be made to abort ARS and the proceed with the firmware update. @remarks A reboot is required to activate the updated firmware image, and it is recommended to ensure ARS runs to completion. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI UpdateFw( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN CHAR16 *pFileName, IN CHAR16 *pWorkingDirectory OPTIONAL, IN BOOLEAN Examine, IN BOOLEAN Force, IN BOOLEAN Recovery, IN BOOLEAN Reserved, OUT NVM_FW_IMAGE_INFO *pFwImageInfo OPTIONAL, OUT COMMAND_STATUS *pCommandStatus ); /** Retrieve the number of regions in the system @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] UseNfit Flag to indicate NFIT usage @param[out] pCount The number of regions found. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetRegionCount( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN BOOLEAN UseNfit, OUT UINT32 *pCount ); /** Retrieve the region list @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Count The number of regions. @param[in] UseNfit Flag to indicate NFIT usage @param[out] pRegions The region info list @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetRegions( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT32 Count, IN BOOLEAN UseNfit, OUT REGION_INFO *pRegions, OUT COMMAND_STATUS *pCommandStatus ); /** Retrieve the details about the region specified with region id @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] RegionId The region id of the region to retrieve @param[out] pRegionInfo A pointer to the region info @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetRegion( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 RegionId, OUT REGION_INFO *pRegionInfo, OUT COMMAND_STATUS *pCommandStatus ); /** Gather info about total capacities on all PMem modules @param[in] pThis a pointer to EFI_DCPMM_CONFIG2_PROTOCOL instance @param[out] pMemoryResourcesInfo structure filled with required information @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetMemoryResourcesInfo( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT MEMORY_RESOURCES_INFO *pMemoryResourcesInfo ); /** Gather info about performance on all PMem modules @param[in] pThis a pointer to EFI_DCPMM_CONFIG2_PROTOCOL instance @param[out] pDimmCount pointer to the number of PMem modules on list @param[out] pDimmsPerformanceData list of PMem modules' performance data @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetDimmsPerformanceData( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pDimmCount, OUT DIMM_PERFORMANCE_DATA **pDimmsPerformanceData ); /** Get System Capabilities information from PCAT tables Pointer to variable length pInterleaveFormatsSupported is allocated here and must be freed by caller. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[out] pSysCapInfo is a pointer to table with System Capabilities information @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetSystemCapabilitiesInfo( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT SYSTEM_CAPABILITIES_INFO *pSysCapInfo ); /** Get PMem module alarm thresholds @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmPid The ID of the PMem module @param[in] SensorId Sensor ID to retrieve information for. See @ref SENSOR_TYPES @param[out] pNonCriticalThreshold Current non-critical threshold for sensor @param[out] pEnabledState Current enable state for sensor @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetAlarmThresholds ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmPid, IN UINT8 SensorId, OUT INT16 *pNonCriticalThreshold, OUT UINT8 *pEnabledState, OUT COMMAND_STATUS *pCommandStatus ); /** Set PMem module alarm thresholds @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] SensorId Sensor id to set values for @param[in] NonCriticalThreshold New non-critical threshold for sensor @param[in] EnabledState New enable state for sensor @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI SetAlarmThresholds ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, IN UINT8 SensorId, IN INT16 NonCriticalThreshold, IN UINT8 EnabledState, OUT COMMAND_STATUS *pCommandStatus ); /** Get PMem module Health Info This FW command is used to retrieve current health of system, including SMART information: * Overall health status * Temperature * Alarm Trips set (Temperature/Spare Blocks) * Device life span as a percentage * Latched Last shutdown status * Unlatched Last shutdown status * Dirty shutdowns * Last shutdown time * AIT DRAM status * Power Cycles (does not include warm resets or S3 resumes) * Power on time (life of PMem module has been powered on) * Uptime for current power cycle in seconds @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmPid The ID of the PMem module @param[out] pHealthInfo pointer to structure containing all Health and Smart variables @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetSmartAndHealth ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmPid, OUT SMART_AND_HEALTH_INFO *pHealthInfo ); /** Get Driver API Version @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pVersion output version in string format MM.mm. M = Major, m = minor. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetDriverApiVersion( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT CHAR16 pVersion[FW_API_VERSION_LEN] ); /** Get namespaces info @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pNamespaceListNode Pointer to namespace list node of @ref NAMESPACE_INFO structs. @param[out] pNamespacesCount Count of namespaces on the list @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetNamespaces ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN OUT LIST_ENTRY *pNamespaceListNode, OUT UINT32 *pNamespacesCount, OUT COMMAND_STATUS *pCommandStatus ); /** Get actual Region goal capacities that would be used based on input values. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[in] PersistentMemType Persistent memory type @param[in, out] pVolatilePercent Volatile region size in percents. @param[in] ReservedPercent Amount of AppDirect memory to not map in percents @param[in] ReserveDimm Reserve one PMem module for use as a not interleaved AppDirect memory @param[out] pConfigGoals pointer to output array @param[out] pConfigGoalsCount number of elements written @param[out] pNumOfDimmsTargeted number of PMem modules targeted in a goal config request @param[out] pMaxPMInterleaveSetsPerDie pointer to Maximum PM Interleave Sets per Die @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All Ok **/ EFI_STATUS EFIAPI GetActualRegionsGoalCapacities( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds OPTIONAL, IN UINT32 SocketIdsCount, IN UINT8 PersistentMemType, IN OUT UINT32 *pVolatilePercent, IN UINT32 ReservedPercent, IN UINT8 ReserveDimm, OUT REGION_GOAL_PER_DIMM_INFO *pConfigGoals, OUT UINT32 *pConfigGoalsCount, OUT UINT32 *pNumOfDimmsTargeted OPTIONAL, OUT UINT32 *pMaxPMInterleaveSetsPerDie OPTIONAL, OUT COMMAND_STATUS *pCommandStatus ); /** Create region goal configuration @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] Examine Do a dry run if set @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[in] PersistentMemType Persistent memory type @param[in] VolatilePercent Volatile region size in percents @param[in] ReservedPercent Amount of AppDirect memory to not map in percents @param[in] ReserveDimm Reserve one PMem module for use as a not interleaved AppDirect memory @param[in] LabelVersionMajor Major version of label to init @param[in] LabelVersionMinor Minor version of label to init @param[out] pMaxPMInterleaveSetsPerDie pointer to Maximum PM Interleave Sets per Die @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_NO_RESPONSE FW busy for one or more PMem modules @retval EFI_SUCCESS All Ok **/ EFI_STATUS EFIAPI CreateGoalConfig ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN BOOLEAN Examine, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds OPTIONAL, IN UINT32 SocketIdsCount, IN UINT8 PersistentMemType, IN UINT32 VolatilePercent, IN UINT32 ReservedPercent, IN UINT8 ReserveDimm, IN UINT16 LabelVersionMajor, IN UINT16 LabelVersionMinor, OUT UINT32 *pMaxPMInterleaveSetsPerDie OPTIONAL, OUT COMMAND_STATUS *pCommandStatus ); /** Delete region goal configuration @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI DeleteGoalConfig ( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds OPTIONAL, IN UINT32 SocketIdsCount, OUT COMMAND_STATUS *pCommandStatus ); /** Get region goal configuration @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[in] ConfigGoalTableSize Number of elements in the pConfigGoals array passed in @param[out] pConfigGoals pointer to output array @param[out] pConfigGoalsCount number of elements written @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetGoalConfigs( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds OPTIONAL, IN UINT32 SocketIdsCount, IN CONST UINT32 ConfigGoalTableSize, OUT REGION_GOAL_PER_DIMM_INFO *pConfigGoals, OUT UINT32 *pConfigGoalsCount, OUT COMMAND_STATUS *pCommandStatus ); /** Dump region goal configuration into the file @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pFilePath is a pointer to a dump file path @param[in] pDevicePath is a pointer to a device where dump file will be stored @param[out] pCommandStatus structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI DumpGoalConfig( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN CHAR16 *pFilePath, IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath, OUT COMMAND_STATUS *pCommandStatus ); /** Load region goal configuration from file @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] pSocketIds Pointer to an array of Socket IDs @param[in] SocketIdsCount Number of items in array of Socket IDs @param[in] pFileString Buffer for Region Goal configuration from file @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI LoadGoalConfig( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, IN UINT16 *pSocketIds, IN UINT32 SocketIdsCount, IN CHAR8 *pFileString, OUT COMMAND_STATUS *pCommandStatus ); /** Start Diagnostic Tests with Detail parameter @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds Pointer to an array of PMem module IDs @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] DiagnosticTests bitfield with selected diagnostic tests to be started @param[in] DimmIdPreference Preference for the PMem module ID (handle or UID) @param[out] ppResult Pointer to the structure with information about test @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI StartDiagnostic( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN CONST UINT8 DiagnosticTests, IN UINT8 DimmIdPreference, OUT DIAG_INFO **ppResultStr ); /** Create namespace Creates a AppDirect namespace on the provided region/PMem module. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] RegionId the ID of the region that the Namespace is supposed to be created. @param[in] Reserved @param[in] BlockSize the size of each of the block in the device. Valid block sizes are: 1 (for AppDirect Namespace), 512 (default), 514, 520, 528, 4096, 4112, 4160, 4224. @param[in] BlockCount the amount of block that this namespace should consist @param[in] pName - Namespace name. @param[in] Mode - boolean value to decide when the namespace should have the BTT arena included @param[in] ForceAll Suppress all warnings @param[in] ForceAlignment Suppress alignment warnings @param[out] pActualNamespaceCapacity capacity needed to meet alignment requirements @param[out] pNamespaceId Pointer to the ID of the namespace that is created @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI CreateNamespace( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 RegionId, IN UINT16 Reserved, IN UINT32 BlockSize, IN UINT64 BlockCount, IN CHAR8 *pName, IN BOOLEAN Mode, IN BOOLEAN ForceAll, IN BOOLEAN ForceAlignment, OUT UINT64 *pActualNamespaceCapacity, OUT UINT16 *pNamespaceId, OUT COMMAND_STATUS *pCommandStatus ); /** Delete namespace Deletes a block or persistent memory namespace. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] Force Force to perform deleting namespace configs on all affected PMem modules @param[in] NamespaceId the ID of the namespace to be removed. @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI DeleteNamespace( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN BOOLEAN Force, IN UINT16 NamespaceId, OUT COMMAND_STATUS *pCommandStatus ); /** Get Error log for given PMem module @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds - array of PMem module pids. Use all PMem modules if pDimms is NULL and DimmsCount is 0. @param[in] DimmsCount - number of PMem modules in array. Use all PMem modules if pDimms is NULL and DimmsCount is 0. @param[in] ThermalError - TRUE = Thermal error, FALSE = media error @param[in] SequenceNumber - sequence number of error to fetch in queue @param[in] HighLevel - high level if true, low level otherwise @param[in, out] pErrorLogCount - IN: element count of pErrorLogs. OUT: Count of error entries in pErrorLogs @param[out] pErrorLogs - output array of errors. Allocated to element count indicated by pErrorLogCount @param[out] pCommandStatus Structure containing detailed NVM error codes. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetErrorLog( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN CONST UINT32 DimmsCount, IN CONST BOOLEAN ThermalError, IN CONST UINT16 SequenceNumber, IN CONST BOOLEAN HighLevel, IN OUT UINT32 *pErrorLogCount, OUT ERROR_LOG_INFO *pErrorLogs, OUT COMMAND_STATUS *pCommandStatus ); /** Get the debug log from a specified PMem module and fw debug log source Note: The caller is responsible for freeing the returned buffer @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmID identifier of what PMem module to get log pages from @param[in] LogSource debug log source buffer to retrieve @param[in] Reserved for future use. Must be 0 for now. @param[out] ppDebugLogBuffer an allocated buffer containing the raw debug log @param[out] pDebugLogBufferSize the size of the raw debug log buffer @param[out] pCommandStatus structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS EFIAPI GetFwDebugLog( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, IN UINT8 LogSource, IN UINT32 Reserved, OUT VOID **ppDebugLogBuffer, OUT UINTN *pDebugLogBufferSize, OUT COMMAND_STATUS *pCommandStatus ); /** Set Optional Configuration Data Policy using FW command @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds - pointer to array of UINT16 PMem module ids to set @param[in] DimmIdsCount - number of elements in pDimmIds @param[in] Reserved @param[in] AveragePowerReportingTimeConstant - (FIS 2.1 and greater) AveragePowerReportingTimeConstant value to set @param[out] pCommandStatus Structure containing detailed NVM error codes. @retval EFI_UNSUPPORTED Mixed Sku of PMem modules has been detected in the system @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok @retval EFI_NO_RESPONSE FW busy for one or more PMem modules **/ EFI_STATUS EFIAPI SetOptionalConfigurationDataPolicy( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds OPTIONAL, IN UINT32 DimmIdsCount, IN UINT8 *Reserved, IN UINT32 *pAveragePowerReportingTimeConstant, OUT COMMAND_STATUS *pCommandStatus ); /** Get requested number of specific PMem module registers for given PMem module id @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmId ID of a PMem module. @param[out] pBsr Pointer to buffer for Boot Status register, contains high and low 4B register. @param[out] Reserved @param[out] pCommandStatus Structure containing detailed NVM error codes. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI RetrieveDimmRegisters( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmId, OUT UINT64 *pBsr, OUT UINT8 *Reserved, OUT COMMAND_STATUS *pCommandStatus ); /** Pass Through Command to FW Sends a command to FW and waits for response from firmware NOTE: Available only in debug driver. @param[in,out] pCmd A firmware command structure @param[in] Timeout The timeout, in 100ns units, to use for the execution of the protocol command. A Timeout value of 0 means that this function will wait indefinitely for the protocol command to execute. If Timeout is greater than zero, then this function will return EFI_TIMEOUT if the time required to execute the receive data command is greater than Timeout. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI PassThruCommand( IN OUT NVM_FW_CMD *pCmd, IN UINT64 Timeout ); /** Attempt to format a PMem module through a customer format command @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] pDimmIds is a pointer to an array of PMem module IDs - if NULL, execute operation on all PMem modules @param[in] DimmIdsCount Number of items in array of PMem module IDs @param[in] Recovery - Perform on non-functional PMem modules @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI DimmFormat( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, IN BOOLEAN Recovery, OUT COMMAND_STATUS *pCommandStatus ); /** Get Total PMem module Volatile, AppDirect, Unconfigured, Reserved and Inaccessible capacities @param[in] pDimms The head of the dimm list @param[out] pRawCapacity pointer to raw capacity @param[out] pVolatileCapacity pointer to volatile capacity @param[out] pAppDirectCapacity pointer to appdirect capacity @param[out] pUnconfiguredCapacity pointer to unconfigured capacity @param[out] pReservedCapacity pointer to reserved capacity @param[out] pInaccessibleCapacity pointer to inaccessible capacity @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_LOAD_ERROR PCD CCUR table missing in one or more PMem modules @retval EFI_SUCCESS Success **/ EFI_STATUS GetTotalDcpmmCapacities( IN LIST_ENTRY *pDimms, OUT UINT64 *pRawCapacity, OUT UINT64 *pVolatileCapacity, OUT UINT64 *pAppDirectCapacity, OUT UINT64 *pUnconfiguredCapacity, OUT UINT64 *pReservedCapacity, OUT UINT64 *pInaccessibleCapacity ); /** Gather capacities from Pmem module @param[in] DimmPid The ID of the PMem module @param[out] pRawCapacity pointer to raw capacity @param[out] pVolatileCapacity pointer to volatile capacity @param[out] pAppDirectCapacity pointer to appdirect capacity @param[out] pUnconfiguredCapacity pointer to unconfigured capacity @param[out] pReservedCapacity pointer to reserved capacity @param[out] pInaccessibleCapacity pointer to inaccessible capacity @retval EFI_INVALID_PARAMETER passed NULL argument @retval Other errors failure of FW commands @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI GetDcpmmCapacities( IN UINT16 DimmPid, OUT UINT64 *pRawCapacity, OUT UINT64 *pVolatileCapacity, OUT UINT64 *pAppDirectCapacity, OUT UINT64 *pUnconfiguredCapacity, OUT UINT64 *pReservedCapacity, OUT UINT64 *pInaccessibleCapacity ); /** Calculate the number of channels with at least one usable DDR for 1LM+2LM @param[in] SocketId Socket Id @param[in] VolatileMode BIOS Volatile Mode @param[out] pChannelCount Pointer to Channel Count @retval EFI_INVALID_PARAMETER Passed NULL argument @retval EFI_LOAD_ERROR Failure to calculate DDR memory size @retval EFI_SUCCESS Success **/ EFI_STATUS GetChannelCountWithUsableDDRCache( IN UINT16 SocketId, OUT UINT32 *pChannelCount ); /** Calculate the total unusable DDR Cache Size in bytes for 2LM @param[in] SocketId Socket Id, 0xFFFF indicates all sockets @param[out] pTotalUnusableDDRCacheSize Pointer to total unusable DDR cache size for 2LM @retval EFI_INVALID_PARAMETER Passed NULL argument @retval EFI_LOAD_ERROR Failure to calculate total unusable DDR Cache Size @retval EFI_SUCCESS Success **/ EFI_STATUS GetUnusableDDRCacheSizeFor2LM( IN UINT16 SocketId, OUT UINT64 *pTotalUnusableDDRCacheSize ); /** Calculate the total DDR Cache Size @param[in] SocketId Socket Id @param[in] VolatileMode BIOS Volatile Mode @param[out] pTotalDDRCacheSize Pointer to total DDR Cache Size @retval EFI_INVALID_PARAMETER Passed NULL argument @retval EFI_NOT_FOUND Failure Unsupported VolatileMode @retval EFI_UNSUPPORTED Failure to calculate total DDR Cache Size @retval EFI_SUCCESS Success **/ EFI_STATUS GetTotalUsableDDRCacheSize( IN UINT16 SocketId, IN MEMORY_MODE VolatileMode, OUT UINT64 *pTotalDDRCacheSize ); /** Retrieve and calculate DDR cache and memory capacity to return. @param[in] SocketId Socket Id, value 0xFFFF indicates include all socket values @param[out] pDDRRawCapacity Pointer to value of the total cache capacity @param[out] pDDRCacheCapacity Pointer to value of the DDR cache capacity @param[out] pDDRVolatileCapacity Pointer to value of the DDR memory capacity @param[out] pDDRInaccessibleCapacity Pointer to value of the DDR inaccessible capacity @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_DEVICE_ERROR Total Intel(R) Optane(TM) persistent memory Persistent and Volatile capacity is larger than total mapped memory @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI GetDDRCapacities( IN UINT16 SocketId, OUT UINT64 *pDDRRawCapacity, OUT UINT64 *pDDRCacheCapacity OPTIONAL, OUT UINT64 *pDDRVolatileCapacity OPTIONAL, OUT UINT64 *pDDRInaccessibleCapacity OPTIONAL ); /** Calculate the total size of available memory in the PMem modules according to the smbios and return the result @param[in] SocketId Socket Id, value 0xFFFF indicates include all socket values @param[out] pResult Pointer to total memory size @retval EFI_INVALID_PARAMETER Passed NULL argument @retval EFI_LOAD_ERROR Failure to calculate DDR memory size @retval EFI_SUCCESS Success **/ EFI_STATUS GetDDRPhysicalSize( IN UINT16 SocketId, OUT UINT64 *pResult ); /** Get system topology from System Management BIOS (SMBIOS) table. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] ppTopologyDimm Structure containing information about DDR entries from SMBIOS. @param[out] pTopologyDimmsNumber Number of DDR entries found in SMBIOS. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetSystemTopology( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT TOPOLOGY_DIMM_INFO **ppTopologyDimm, OUT UINT16 *pTopologyDimmsNumber ); /** Get the system-wide ARS status for the persistent memory capacity of the system. In this function, the system-wide ARS status is determined based on the ARS status values for the individual PMem modules. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pARSStatus pointer to the current system ARS status. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetARSStatus( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT8 *pARSStatus ); /** Get the User Driver Preferences. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[out] pDriverPreferences pointer to the current driver preferences. @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h)k **/ EFI_STATUS EFIAPI GetDriverPreferences( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT DRIVER_PREFERENCES *pDriverPreferences, OUT COMMAND_STATUS *pCommandStatus ); /** Set the User Driver Preferences. @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDriverPreferences pointer to the desired driver preferences. @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI SetDriverPreferences( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN DRIVER_PREFERENCES *pDriverPreferences, OUT COMMAND_STATUS *pCommandStatus ); /** Get DDRT IO init info @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] DimmID DimmID of device to retrieve support data from @param[out] pDdrtTrainingStatus pointer to the PMem modules DDRT training status @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetDdrtIoInitInfo( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT UINT8 *pDdrtTrainingStatus ); /** Get long operation status @param[in] pThis Pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] DimmID DimmID of device to retrieve status from @param[in] pOpcode pointer to opcode of long op command to check @param[in] pSubOpcode pointer to subopcode of long op command to check @param[out] pPercentComplete pointer to percentage current command has completed @param[out] pEstimatedTimeLeft pointer to time to completion BCD @param[out] pFwStatus pointer to completed mailbox status code @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetLongOpStatus( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT UINT8 *pOpcode OPTIONAL, OUT UINT8 *pSubOpcode OPTIONAL, OUT UINT16 *pPercentComplete OPTIONAL, OUT UINT32 *pEstimatedTimeLeft OPTIONAL, OUT EFI_STATUS *pFwStatus ); /** InjectError @param[in] pThis is a pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] pDimmIds - pointer to array of UINT16 PMem module ids to get data for @param[in] DimmIdsCount - number of elements in pDimmIds @param[in] ErrorInjType - Error Inject type @param[in] ClearStatus - Is clear status set @param[in] pInjectTemperatureValue - Pointer to inject temperature @param[in] pInjectPoisonAddress - Pointer to inject poison address @param[in] pPoisonType - Pointer to poison type @param[in] pPercentageRemaining - Pointer to percentage remaining @param[out] pCommandStatus Structure containing detailed NVM error codes. @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI InjectError( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 *pDimmIds, IN UINT32 DimmIdsCount, IN UINT8 ErrorInjType, IN UINT8 ClearStatus, IN UINT64 *pInjectTemperatureValue OPTIONAL, IN UINT64 *pInjectPoisonAddress, IN UINT8 *pPoisonType, IN UINT8 *pPercentageRemaining, OUT COMMAND_STATUS *pCommandStatus ); /** GetBsr value and return bsr or boot status bitmask depending on the requested options UEFI - Read directly from BSR register OS - Get BSR value from BIOS emulated command @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmID PMem module handle of the PMem module @param[out] pBsrValue pointer to BSR register value OPTIONAL @param[out] pBootStatusBitMask pointer to BootStatusBitmask OPTIONAL @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_NO_RESPONSE BSR value returned by FW is invalid @retval EFI_SUCCESS Success @retval Other errors failure of FW commands **/ EFI_STATUS EFIAPI GetBSRAndBootStatusBitMask( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT UINT64 *pBsrValue OPTIONAL, OUT UINT16 *pBootStatusBitmask OPTIONAL ); /** Verify target DimmIds list. Fill output list of pointers to PMem modules. If sockets were specified then get all PMem modules from these sockets. If PMem module Ids were provided then check if those PMem modules exist. If there are duplicate PMem module/socket Ids then report error. If specified PMem modules count is 0 then take all Manageable PMem modules. Update CommandStatus structure with any warnings/errors found. @param[in] DimmIds An array of PMem module Ids @param[in] DimmIdsCount Number of items in array of PMem module Ids @param[in] SocketIds An array of Socket Ids @param[in] SocketIdsCount Number of items in array of Socket Ids @param[in] RequireDcpmmsBitfield Indicate what requirements should be validated on the list of PMem modules discovered. @param[out] pDimms Output array of pointers to verified PMem modules @param[out] pDimmsNum Number of items in array of pointers to PMem modules @param[out] pCommandStatus Pointer to command status structure @retval EFI_INVALID_PARAMETER Problem with getting specified PMem modules @retval EFI_SUCCESS All Ok **/ EFI_STATUS EFIAPI VerifyTargetDimms ( IN UINT16 DimmIds[] OPTIONAL, IN UINT32 DimmIdsCount, IN UINT16 SocketIds[] OPTIONAL, IN UINT32 SocketIdsCount, IN REQUIRE_DCPMMS RequireDcpmmsBitfield, OUT DIMM *pDimms[MAX_DIMMS], OUT UINT32 *pDimmsNum, OUT COMMAND_STATUS *pCommandStatus ); /** Verify target DimmIds in list are available for SPI Flash. If PMem module Ids were provided then check if those PMem modules exist in a SPI flashable state and return list of verified PMem modules. If specified PMem modules count is 0 then return all PMem modules that are in SPI Flashable state. Update CommandStatus structure at the end. @param[in] DimmIds An array of PMem module Ids @param[in] DimmIdsCount Number of items in array of PMem module Ids @param[out] pDimms Output array of pointers to verified PMem modules @param[out] pDimmsNum Number of items in array of pointers to PMem modules @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS Success @retval EFI_NOT_FOUND a PMem module in DimmIds is not in a flashable state or no PMem modules found @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI VerifyNonfunctionalTargetDimms( IN UINT16 DimmIds[] OPTIONAL, IN UINT32 DimmIdsCount, OUT DIMM *pDimms[MAX_DIMMS], OUT UINT32 *pDimmsNum, OUT COMMAND_STATUS *pCommandStatus ); /** Set object status for PMem modules not paired with DDR in case of 2LM @param[in] pDimms Array of pointers to targeted PMem modules only @param[in] pDimmsNum Number of pointers in pDimms @param[out] pCommandStatus Pointer to command status structure @retval EFI_LOAD_ERROR Error in retrieving information from ACPI tables @retval EFI_INVALID_PARAMETER pCommandStatus is NULL @retval EFI_SUCCESS All Ok **/ EFI_STATUS SetObjStatusForPMemNotPairedWithDdr( IN DIMM *pDimms[MAX_DIMMS], IN UINT32 DimmsNum, OUT COMMAND_STATUS *pCommandStatus ); /** Examine a given PMem module to see if a long op is in progress and report it back @param[in] pDimm The PMem module to check the status of @param[out] pNvmStatus The status of the PMem module's long op status. NVM_SUCCESS = No long op status is under way. @retval EFI_SUCCESS if the request for long op status was successful (whether a long op status is under way or not) @retval EFI_... the error preventing the check for the long op status **/ EFI_STATUS CheckForLongOpStatusInProgress( IN DIMM *pDimm, OUT NVM_STATUS *pNvmStatus ); /** Get Command Access Policy is used to retrieve a list of FW commands that may be restricted. Passing pCapInfo as NULL will provide the maximum number of possible return elements by updating pCount. @param[in] pThis A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmID Handle of the PMem module @param[in,out] pCount IN: Count is number of elements in the pCapInfo array. OUT: number of elements written to pCapInfo @param[out] pCapInfo Array of Command Access Policy Entries. If NULL, pCount will be updated with maximum number of elements possible. OPTIONAL @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetCommandAccessPolicy( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, IN OUT UINT32 *pCount, OUT COMMAND_ACCESS_POLICY_ENTRY *pCapInfo OPTIONAL ); /** Get Command Effect Log is used to retrieve a list of FW commands and their effects on the PMem module subsystem. @param[in] pThis - A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance. @param[in] DimmID - Handle of the PMem module @param[out] ppLogEntry - A pointer to the CEL entry table for a given PMem module. @param[out] pEntryCount - The number of CEL entries @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetCommandEffectLog( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT COMMAND_EFFECT_LOG_ENTRY **ppLogEntry, OUT UINT32 *pEntryCount ); #ifndef OS_BUILD /** This function makes calls to the PMem modules required to initialize the driver. @retval EFI_SUCCESS if no errors. @retval EFI_xxxx depending on error encountered. **/ EFI_STATUS LoadArsList(); #endif /** Gets value of transport protocol and payload size settings from platform @param[in] pThis A pointer to EFI DCPMM CONFIG PROTOCOL structure @param[in,out] pAttribs A pointer to a variable used to store protocol and payload settings @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid FW Command Parameter. **/ EFI_STATUS EFIAPI GetFisTransportAttributes( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN OUT EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS *pAttribs ); /** Sets value of transport protocol and payload size settings for platform @param[in] pThis A pointer to EFI DCPMM CONFIG PROTOCOL structure @param[in] Attribs The new value to assign to protocol and payload settings @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid FW Command Parameter. **/ EFI_STATUS EFIAPI SetFisTransportAttributes( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN EFI_DCPMM_CONFIG_TRANSPORT_ATTRIBS Attribs ); /** Get minimal FWImageMaxSize value for all designated DIMMs @param[in] pNvmDimmConfigProtocol - The open config protocol @param[in] pDimms - Pointer to an array of DIMMs @param[in] DimmsNum - Number of items in array of DIMMs @retval The minimal allowed size of firmware image buffer in bytes @retval MAX_UINT64 is an error **/ UINT64 GetMinFWImageMaxSize( IN EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol, IN DIMM *pDimms[MAX_DIMMS], IN UINT32 DimmsNum ); /* * Helper function for initializing information from the System Management BIOS (SMBIOS). */ EFI_STATUS FillSmbiosInfo( IN OUT DIMM_INFO *pDimmInfo ); #ifndef OS_BUILD /** Gets value of PcdDebugPrintErrorLevel for the pmem driver @param[in] pThis A pointer to EFI DCPMM CONFIG PROTOCOL structure @param[out] ErrorLevel A pointer used to store the value of debug print error level @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid ErrorLevel Parameter. **/ EFI_STATUS EFIAPI GetDriverDebugPrintErrorLevel( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, OUT UINT32 *pErrorLevel ); /** Sets value of PcdDebugPrintErrorLevel for the pmem driver @param[in] pThis A pointer to EFI DCPMM CONFIG PROTOCOL structure @param[in] ErrorLevel The new value to assign to debug print error level @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER Invalid ErrorLevel Parameter. **/ EFI_STATUS EFIAPI SetDriverDebugPrintErrorLevel( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT32 ErrorLevel ); #endif //OS_BUILD /** Get FIPS Mode retrieves the current FIPS Mode for the DimmID provided. @param[in] pThis - A pointer to the EFI_DCPMM_CONFIG2_PROTOCOL instance @param[in] DimmID - Handle of the PMem module @param[out] pFIPSMode - A pointer to a FIPS_MODE struct to fill in @param[out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS Success @retval ERROR any non-zero value is an error (more details in Base.h) **/ EFI_STATUS EFIAPI GetFIPSMode( IN EFI_DCPMM_CONFIG2_PROTOCOL *pThis, IN UINT16 DimmID, OUT FIPS_MODE *pFIPSMode, OUT COMMAND_STATUS *pCommandStatus ); #endif /* _NVMDIMM_CONFIG_H_ */ ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Driver/NvmDimmConfigInt.h000066400000000000000000000221771440615110200250210ustar00rootroot00000000000000/* * Copyright (c) 2015-2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file NvmDimmConfigInt.h * @brief Internal header file for the NvmDimmConfig */ #ifndef _NVMDIMM_CONFIG_INT_H_ #define _NVMDIMM_CONFIG_INT_H_ /** Retrieve the User Driver Preferences from RunTime Services. @param[out] pDriverPreferences pointer to the current driver preferences. @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_INVALID_PARAMETER One or more parameters are invalid @retval EFI_SUCCESS All ok **/ EFI_STATUS ReadRunTimeDriverPreferences( OUT DRIVER_PREFERENCES *pDriverPreferences ); /** Sorts the region list by Id @param[in out] pRegion1 A pointer to the Regions. @param[in out] pRegion2 A pointer to the copy of Regions. @retval int returns 0,-1, 0 **/ INT32 SortRegionInfoById(VOID *pRegion1, VOID *pRegion2); /** Sorts the DimmIds list by Id @param[in out] pDimmId1 A pointer to the pDimmId list. @param[in out] pDimmId2 A pointer to the copy of pDimmId list. @retval int returns 0,-1, 0 **/ INT32 SortRegionDimmId(VOID *pDimmId1, VOID *pDimmId2); /** Validate firmware Image version @param[in] pImage the FW Image header @param[in] Force is a BOOL which indicates whether to skip prompts @param[in] pDimm Pointer to the Dimm whose FW is validated @param[out] pNvmStatus NVM status code @retval EFI_ABORTED One of the checks failed @retval EFI_OUT_OF_RESOURCES Unable to allocate memory for a data structure @retval EFI_SUCCESS Update has completed successfully **/ EFI_STATUS ValidateImageVersion( IN NVM_FW_IMAGE_HEADER *pImage, IN BOOLEAN Force, IN DIMM *pDimm, OUT NVM_STATUS *pNvmStatus, OUT COMMAND_STATUS *pCommandStatus ); /** Parse EFI_ACPI_DESCRIPTION_HEADER (DSDT) and fetch NFIT & PCAT pointers to table Also, parse PMTT table to check if MM can be configured @param[in] pDsdt a pointer to EFI_ACPI_DESCRIPTION_HEADER instance for each of NFIT, PMTT and PCAT @param[out] ppFitHead pointer to pointer to store NFIT table @param[out] ppPcatHead pointer to pointer to store PCAT table @param[out] ppPmttHead pointer to pointer to store PMTT table @param[out] pIsMemoryModeAllowed pointer to check if MM can be configured @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_DEVICE_ERROR could not parse at least one of the tables @retval EFI_SUCCESS Success **/ EFI_STATUS ParseAcpiTables( IN CONST EFI_ACPI_DESCRIPTION_HEADER *pNfit, IN CONST EFI_ACPI_DESCRIPTION_HEADER *pPcat, IN CONST EFI_ACPI_DESCRIPTION_HEADER *pPMTT, OUT ParsedFitHeader **ppFitHead, OUT ParsedPcatHeader **ppPcatHead, OUT ParsedPmttHeader **ppPmttHead, OUT BOOLEAN *pIsMemoryModeAllowed ); /** Fetch the NFIT and PCAT tables from EFI_SYSTEM_TABLE @param[in] pSystemTable is a pointer to the EFI_SYSTEM_TABLE instance @param[out] ppNfit is a pointer to EFI_ACPI_DESCRIPTION_HEADER (NFIT) @param[out] ppPcat is a pointer to EFI_ACPI_DESCRIPTION_HEADER (PCAT) @param[out] ppPMTT is a pointer to EFI_ACPI_DESCRIPTION_HEADER (PMTT) @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER NULL argument @retval EFI_LOAD_ERROR if one or more of the tables could not be found **/ EFI_STATUS GetAcpiTables( IN CONST EFI_SYSTEM_TABLE *pSystemTable, OUT EFI_ACPI_DESCRIPTION_HEADER **ppNfit, OUT EFI_ACPI_DESCRIPTION_HEADER **ppPcat, OUT EFI_ACPI_DESCRIPTION_HEADER **ppPMTT ); /** Get the PCI base address from MCFG table from EFI_SYSTEM_TABLE @param[in] pSystemTable is a pointer to the EFI_SYSTEM_TABLE instance @param[out] pPciBaseAddress is a pointer to the Base Address @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER NULL argument @retval EFI_LOAD_ERROR if one or more of the tables could not be found **/ EFI_STATUS GetPciBaseAddress( IN CONST EFI_SYSTEM_TABLE *pSystemTable, OUT UINT64 *pPciBaseAddress ); /** Check the memory map against the NFIT SPA memory for consistency @retval EFI_SUCCESS on success @retval EFI_OUT_OF_RESOURCES for a failed allocation @retval EFI_BAD_BUFFER_SIZE if the nfit spa memory is more than the one in memmap **/ EFI_STATUS CheckMemoryMap( ); /** Initialize ACPI tables (NFit and PCAT) @retval EFI_SUCCESS on success @retval EFI_NOT_FOUND Nfit table not found **/ EFI_STATUS initAcpiTables( ); #ifdef OS_BUILD /** Uninitialize ACPI tables (NFit and PCAT) @retval EFI_SUCCESS on success @retval EFI_NOT_FOUND Nfit table not found **/ EFI_STATUS uninitAcpiTables( ); #endif // OS_BUILD /** Initialize a simulated NFit table. @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES cannot allocate memory for NFit table @retval EFI_LOAD_ERROR problem appears when loading data to Nfit table **/ EFI_STATUS initSimulatedNFit( ); /** Write simulated data to PCD tables on NVDIMMs @retval EFI_SUCCESS @retval EFI_OUT_OF_RESOURCES cannot allocate memory for NFit table @retval EFI_LOAD_ERROR problem appears when loading data to Nfit table **/ EFI_STATUS InitSimulatedPcd( ); /** Parse ACPI tables and create DIMM list @retval EFI_SUCCESS Success @retval EFI_... Other errors from subroutines **/ EFI_STATUS FillDimmList( ); /** Clean up the in memory DIMM inventory @retval EFI_SUCCESS Success @retval EFI_... Other errors from subroutines **/ EFI_STATUS FreeDimmList( ); /** Retrieve Smbios tables dynamically, and populate Smbios table structures of type 17/20 for the specified Dimm Pid @param[in] DimmPid The ID of the DIMM @param[out] pDmiPhysicalDev Pointer to smbios table structure of type 17 @param[out] pDmiDeviceMappedAddr Pointer to smbios table structure of type 20 @retval EFI_INVALID_PARAMETER passed NULL argument @retval EFI_DEVICE_ERROR Failure to retrieve SMBIOS tables from gST @retval EFI_SUCCESS Success **/ EFI_STATUS GetDmiMemdevInfo( IN UINT16 DimmPid, OUT SMBIOS_STRUCTURE_POINTER *pDmiPhysicalDev, OUT SMBIOS_STRUCTURE_POINTER *pDmiDeviceMappedAddr, OUT SMBIOS_VERSION *pSmbiosVersion ); /** Automatically provision capacity Decision logic for when to automatically provision capacity based on ProvisionCapacityStatus, PCD data and topology change. If automatic provisioning is triggered and succeeds, this function will reboot and never return. @param[in] pIntelDIMMConfig Pointer to struct containing EFI vars @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS AutomaticProvisionCapacity( IN INTEL_DIMM_CONFIG *pIntelDIMMConfig ); /** Automatically provision namespaces Decision logic for when to automatically provision namespaces based on ProvisionNamespaceStatus. @param[in] pIntelDIMMConfig Pointer to struct containing EFI vars @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS AutomaticProvisionNamespace( IN INTEL_DIMM_CONFIG *pIntelDIMMConfig ); /** Checks inputs and executes create goal Will remove all namespaces. @param[in] pIntelDIMMConfig Pointer to struct containing EFI vars @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS AutomaticCreateGoal( IN INTEL_DIMM_CONFIG *pIntelDIMMConfig ); /** Checks inputs and executes create namespace on empty ISets @param[in] pIntelDIMMConfig Pointer to struct containing EFI vars @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS AutomaticCreateNamespace( IN INTEL_DIMM_CONFIG *pIntelDIMMConfig ); /** Retrieves Intel Dimm Config EFI vars User is responsible for freeing ppIntelDIMMConfig @param[out] pIntelDIMMConfig Pointer to struct to fill with EFI vars @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS RetrieveIntelDIMMConfig( OUT INTEL_DIMM_CONFIG **ppIntelDIMMConfig ); /** Updates IntelDIMMConfig EFI Vars @param[in] pIntelDIMMConfig pointer to new config to write **/ VOID UpdateIntelDIMMConfig( IN INTEL_DIMM_CONFIG *pIntelDIMMConfig ); /** Checks if PCD copy of vars matches EFI on all DIMMs If a DIMM is missing PCD data then it is considered not matching @param[in] pIntelDIMMConfig Pointer to struct with EFI vars @param[out] pVarsMatch True if PCD data on all DIMMs match @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS CheckPCDAutoConfVars( IN INTEL_DIMM_CONFIG *pIntelDIMMConfigEfiVar, OUT BOOLEAN *pVarsMatch ); /** Checks if the topology has changed based on CCUR config status @param[out] pTopologyChanged True if ConfigStatus indicates topology change @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS CheckTopologyChange( OUT BOOLEAN *pTopologyChanged ); /** Checks if the previous goal was applied successfully @param[out] pGoalSuccess True if goal was applied successfully @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are invalid **/ EFI_STATUS CheckGoalStatus( OUT BOOLEAN *pGoalSuccess ); #endif // _NVMDIMM_CONFIG_INT_H_ ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Namespace/000077500000000000000000000000001440615110200221305ustar00rootroot00000000000000ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Namespace/NvmDimmBlockIo.c000066400000000000000000000176151440615110200251200ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "NvmDimmBlockIo.h" EFI_GUID gNvmDimmBlockIoProtocolGuid = EFI_BLOCK_IO_PROTOCOL_GUID; /** Block I/O Media structure **/ GLOBAL_REMOVE_IF_UNREFERENCED EFI_BLOCK_IO_MEDIA gNvmDimmDriverBlockIoMedia = { 0, //!< MediaId FALSE, //!< RemovableMedia TRUE, //!< MediaPresent FALSE, //!< LogicalPartition FALSE, //!< ReadOnly TRUE, //!< WriteCaching 512, //!< BlockSize - will be overwritten by the namespace actual size /** We do not require any particular IO buffers alignment. The alignment below is a standard one. If in future requested, this value can be changed. **/ 2, //!< IoAlign 0, //!< LastBlock 0, //!< LowestAlignedLba 1, //!< LogicalBlocksPerPhysicalBlock 512 //!< OptimalTransferLengthGranularity }; /** Block I/O Protocol Instance **/ GLOBAL_REMOVE_IF_UNREFERENCED EFI_BLOCK_IO_PROTOCOL gNvmDimmDriverBlockIo = { EFI_BLOCK_IO_PROTOCOL_REVISION3, //!< Revision &gNvmDimmDriverBlockIoMedia, //!< Media (EFI_BLOCK_RESET) NvmDimmDriverBlockIoReset, //!< Reset NvmDimmDriverBlockIoReadBlocks, //!< ReadBlocks NvmDimmDriverBlockIoWriteBlocks, //!< WriteBlocks NvmDimmDriverBlockIoFlushBlocks //!< FlushBlocks }; /** Block I/O 2 Protocol Instance **/ /*GLOBAL_REMOVE_IF_UNREFERENCED EFI_BLOCK_IO2_PROTOCOL gNvmDimmDriverBlockIo2 = { &gNvmDimmDriverBlockIoMedia, //!< Media NvmDimmDriverBlockIoReset, //!< Reset NvmDimmDriverBlockIoReadBlocksEx, //!< ReadBlocks NvmDimmDriverBlockIoWriteBlocksEx, //!< WriteBlocks NvmDimmDriverBlockIoFlushBlocksEx //!< FlushBlocks };*/ /** Read BufferSize bytes from Lba into Buffer. @param pThis Indicates a pointer to the calling context. @param MediaId ID of the media, changes every time the media is replaced. @param Lba The starting LBA to read from. @param BufferSize Size of Buffer, must be a multiple of device block size. @param pBuffer A pointer to the destination buffer for the data. The caller is responsible for either having implicit or explicit ownership of the buffer. @retval EFI_SUCCESS The data was read correctly from the device. @retval EFI_DEVICE_ERROR The device reported an error while performing the read. @retval EFI_NO_MEDIA There is no media in the device. @retval EFI_MEDIA_CHANGED The MediaId does not match the current device. @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, or the buffer is not on proper alignment. **/ EFI_STATUS EFIAPI NvmDimmDriverBlockIoReadBlocks( IN EFI_BLOCK_IO_PROTOCOL *pThis, IN UINT32 MediaId, IN EFI_LBA Lba, IN UINTN BufferSize, OUT VOID *pBuffer ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NAMESPACE *pNamespace = NULL; UINT32 Index = 0; UINT64 BlocksToRead = 0; CHAR8 *pByteBuffer = pBuffer; if (pThis == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (pThis->Media == NULL) { ReturnCode = EFI_NO_MEDIA; goto Finish; } if (MediaId != pThis->Media->MediaId) { ReturnCode = EFI_MEDIA_CHANGED; goto Finish; } if (BufferSize % pThis->Media->BlockSize != 0) { ReturnCode = EFI_BAD_BUFFER_SIZE; goto Finish; } // Zero-length operations are always success if (BufferSize == 0) { ReturnCode = EFI_SUCCESS; goto Finish; } BlocksToRead = BufferSize / pThis->Media->BlockSize; if ((Lba + BlocksToRead - 1) > pThis->Media->LastBlock || pBuffer == NULL || ((UINT64) pBuffer % pThis->Media->IoAlign != 0)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pNamespace = GET_NAMESPACE_INSTANCE(pThis); if (pNamespace == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } for (Index = 0; Index < BlocksToRead; Index++) { ReturnCode = ReadBlockDevice(pNamespace, Lba + Index, pByteBuffer + (Index *pThis->Media->BlockSize)); if (EFI_ERROR(ReturnCode)) { goto Finish; } } Finish: if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error in the read."); } return ReturnCode; } /** Write BufferSize bytes from LBA into Buffer. @param pThis Indicates a pointer to the calling context. @param MediaId The media ID that the write request is for. @param Lba The starting logical block address to be written. The caller is responsible for writing to only legitimate locations. @param BufferSize Size of Buffer, must be a multiple of device block size. @param pBuffer A pointer to the source buffer for the data. @retval EFI_SUCCESS The data was written correctly to the device. @retval EFI_WRITE_PROTECTED The device cannot be written to. @retval EFI_DEVICE_ERROR The device reported an error while performing the write. @retval EFI_NO_MEDIA There is no media in the device. @retval EFI_MEDIA_CHANGED The MediaId does not match the current device. @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, or the buffer is not on proper alignment. **/ EFI_STATUS EFIAPI NvmDimmDriverBlockIoWriteBlocks( IN EFI_BLOCK_IO_PROTOCOL *pThis, IN UINT32 MediaId, IN EFI_LBA Lba, IN UINTN BufferSize, IN VOID *pBuffer ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NAMESPACE *pNamespace = NULL; UINT32 Index = 0; UINT64 BlocksToWrite = 0; CHAR8 *pByteBuffer = pBuffer; if (pThis == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (pThis->Media == NULL) { ReturnCode = EFI_NO_MEDIA; goto Finish; } if (MediaId != pThis->Media->MediaId) { ReturnCode = EFI_MEDIA_CHANGED; goto Finish; } if (BufferSize % pThis->Media->BlockSize != 0) { ReturnCode = EFI_BAD_BUFFER_SIZE; goto Finish; } // Zero-length operations are always success if (BufferSize == 0) { ReturnCode = EFI_SUCCESS; goto Finish; } BlocksToWrite = BufferSize / pThis->Media->BlockSize; if ((Lba + BlocksToWrite - 1) > pThis->Media->LastBlock || pBuffer == NULL || ((UINT64) pBuffer % pThis->Media->IoAlign != 0)) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } pNamespace = GET_NAMESPACE_INSTANCE(pThis); if (pNamespace == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } for (Index = 0; Index < BlocksToWrite; Index++) { ReturnCode = WriteBlockDevice(pNamespace, Lba + Index, pByteBuffer + (Index * pThis->Media->BlockSize)); if (EFI_ERROR(ReturnCode)) { goto Finish; } } Finish: if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Error in the write."); } return ReturnCode; } /** Flush the Block Device. @param pThis Indicates a pointer to the calling context. @retval EFI_SUCCESS All outstanding data was written to the device @retval EFI_DEVICE_ERROR The device reported an error while writing back the data @retval EFI_NO_MEDIA There is no media in the device. @retval EFI_UNSUPPORTED Not supported **/ EFI_STATUS EFIAPI NvmDimmDriverBlockIoFlushBlocks( IN EFI_BLOCK_IO_PROTOCOL *pThis ) { return EFI_SUCCESS; } /** Reset the block device hardware. @param[in] pThis Indicates a pointer to the calling context. @param[in] ExtendedVerification Indicates that the driver may perform a more exhaustive verification operation of the device during reset. @retval EFI_SUCCESS The device was reset. @retval EFI_DEVICE_ERROR The device is not functioning properly and could not be reset. **/ EFI_STATUS EFIAPI NvmDimmDriverBlockIoReset( IN EFI_BLOCK_IO2_PROTOCOL *pThis, IN BOOLEAN ExtendedVerification ) { return EFI_SUCCESS; } ipmctl-03.00.00.0485/DcpmPkg/driver/Protocol/Namespace/NvmDimmBlockIo.h000066400000000000000000000077121440615110200251220ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file NvmDimmBlockIo.h * @brief Implementation of the EFI_BLOCK_IO_PROTOCOL for * Namespaces. */ #ifndef _NVM_DIMM_BLOCK_IO_H_ #define _NVM_DIMM_BLOCK_IO_H_ #include "NvmDimmDriver.h" #include "NvmTypes.h" #include "Namespace.h" #define GET_NAMESPACE_INSTANCE(InstanceAddress) BASE_CR(InstanceAddress, NAMESPACE, BlockIoInstance) /** Read BufferSize bytes from Logical Block Address (LBA) into buffer. @param pThis Indicates a pointer to the calling context. @param MediaId ID of the media, changes every time the media is replaced. @param Lba The starting LBA to read from. @param BufferSize Size of Buffer, must be a multiple of device block size. @param pBuffer A pointer to the destination buffer for the data. The caller is responsible for either having implicit or explicit ownership of the buffer. @retval EFI_SUCCESS The data was read correctly from the device. @retval EFI_DEVICE_ERROR The device reported an error while performing the read. @retval EFI_NO_MEDIA There is no media in the device. @retval EFI_MEDIA_CHANGED The MediaId does not match the current device. @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, or the buffer is not on proper alignment. **/ EFI_STATUS EFIAPI NvmDimmDriverBlockIoReadBlocks( IN EFI_BLOCK_IO_PROTOCOL *pThis, IN UINT32 MediaId, IN EFI_LBA Lba, IN UINTN BufferSize, OUT VOID *pBuffer ); /** Write BufferSize bytes from LBA into Buffer. @param pThis Indicates a pointer to the calling context. @param MediaId The media ID that the write request is for. @param Lba The starting logical block address to be written. The caller is responsible for writing to only legitimate locations. @param BufferSize Size of Buffer, must be a multiple of device block size. @param pBuffer A pointer to the source buffer for the data. @retval EFI_SUCCESS The data was written correctly to the device. @retval EFI_WRITE_PROTECTED The device cannot be written to. @retval EFI_DEVICE_ERROR The device reported an error while performing the write. @retval EFI_NO_MEDIA There is no media in the device. @retval EFI_MEDIA_CHANGED The MediaId does not match the current device. @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, or the buffer is not on proper alignment. **/ EFI_STATUS EFIAPI NvmDimmDriverBlockIoWriteBlocks( IN EFI_BLOCK_IO_PROTOCOL *pThis, IN UINT32 MediaId, IN EFI_LBA Lba, IN UINTN BufferSize, IN VOID *pBuffer ); /** Flush the Block Device. @param pThis Indicates a pointer to the calling context. @retval EFI_SUCCESS All outstanding data was written to the device @retval EFI_DEVICE_ERROR The device reported an error while writing back the data @retval EFI_NO_MEDIA There is no media in the device. @retval EFI_UNSUPPORTED Not supported **/ EFI_STATUS EFIAPI NvmDimmDriverBlockIoFlushBlocks( IN EFI_BLOCK_IO_PROTOCOL *pThis ); /** Reset the block device hardware. @param[in] pThis Indicates a pointer to the calling context. @param[in] ExtendedVerification Indicates that the driver may perform a more exhaustive verification operation of the device during reset. @retval EFI_SUCCESS The device was reset. @retval EFI_DEVICE_ERROR The device is not functioning properly and could not be reset. **/ EFI_STATUS EFIAPI NvmDimmDriverBlockIoReset( IN EFI_BLOCK_IO2_PROTOCOL *pThis, IN BOOLEAN ExtendedVerification ); extern EFI_GUID gNvmDimmBlockIoProtocolGuid; #endif /** _NVM_DIMM_BLOCK_IO_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/driver/Utils/000077500000000000000000000000001440615110200175335ustar00rootroot00000000000000ipmctl-03.00.00.0485/DcpmPkg/driver/Utils/AcpiParsing.c000066400000000000000000002551351440615110200221120ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "AcpiParsing.h" #include #include #include #include #include #include // 2 iMC and 3 channels each - Purley #define IMCS_PER_DIE_2_3 2 #define CHANNELS_PER_IMC_2_3 3 extern NVMDIMMDRIVER_DATA *gNvmDimmData; GUID gSpaRangeVolatileRegionGuid = SPA_RANGE_VOLATILE_REGION_GUID; GUID gSpaRangePmRegionGuid = SPA_RANGE_PM_REGION_GUID; GUID gSpaRangeControlRegionGuid = SPA_RANGE_CONTROL_REGION_GUID; GUID gSpaRangeBlockDataWindowRegionGuid = SPA_RANGE_BLOCK_DATA_WINDOW_REGION_GUID; GUID gSpaRangeRawVolatileRegionGuid = SPA_RANGE_RAW_VOLATILE; GUID gSpaRangeIsoVolatileRegionGuid = SPA_RANGE_ISO_VOLATILE; GUID gSpaRangeRawPmRegionGuid = SPA_RANGE_RAW_PM; GUID gSpaRangeIsoPmRegionGuid = SPA_RANGE_ISO_PM; GUID gAppDirectPmTypeGuid = APPDIRECT_PM_TYPE; GUID gSpaRangeMailboxCustomGuid = SPA_RANGE_MAILBOX_CUSTOM_GUID; GUID gDieTypeDeviceGuid = PMTT_TYPE_DIE_GUID; GUID gChannelTypeDeviceGuid = PMTT_TYPE_CHANNEL_GUID; GUID gSlotTypeDeviceGuid = PMTT_TYPE_SLOT_GUID; /** CopyMemoryAndAddPointerToArray - Copies the data and adds the result pointer to an array of pointers. @param[in, out] ppTable pointer to the pointers. Warning! This pointer will be freed. @param[in] pToAdd pointer to the data that the caller wants to add to the array. @param[in] DataSize size of the data that are supposed to be copied. @param[in] NewPointerIndex index in the table that the new pointer should have. @retval NULL - if a memory allocation failed. @retval pointer to the new array of pointers (with the new one at the end). **/ STATIC VOID ** CopyMemoryAndAddPointerToArray( IN OUT VOID **ppTable, IN VOID *pToAdd, IN UINT32 DataSize, IN UINT32 *pNewPointerIndex ); /** ParseNfitTable - Performs deserialization from binary memory block into parsed structure of pointers. @param[in] pTable pointer to the memory containing the NFIT binary representation. @param[out] ppParsedNfit Pointer to a pointer where the allocated and parsed NFIT table will be stored @retval EFI_INVALID_PARAMETER One of the provided parameters is invalid @retval EFI_VOLUME_CORRUPTED If the table checksum is invalid @retval EFI_INCOMPATIBLE_VERSION If the table is not compatible with this ipmctl version @retval EFI_SUCCESS **/ EFI_STATUS ParseNfitTable( IN VOID *pTable, OUT ParsedFitHeader **ppParsedNfit ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; ParsedFitHeader *pParsedNfit = NULL; NFitHeader *pNFit = NULL; UINT8 *pTabPointer = NULL; SubTableHeader *pTableHeader = NULL; UINT32 RemainingNFITBytes = 0; NVDIMM_ENTRY(); CHECK_NULL_ARG(pTable, Finish); CHECK_NULL_ARG(ppParsedNfit, Finish); pNFit = (NFitHeader *)pTable; if (!IsChecksumValid(pNFit, pNFit->Header.Length, pNFit->Header.Checksum)) { NVDIMM_DBG("The checksum of NFIT table is invalid."); goto Finish; } if (IS_NFIT_REVISION_INVALID(pNFit->Header.Revision)) { NVDIMM_DBG("NFIT table revision is invalid"); ReturnCode = EFI_INCOMPATIBLE_VERSION; goto Finish; } pTabPointer = (UINT8 *)pTable + sizeof(NFitHeader); pTableHeader = (SubTableHeader *)pTabPointer; RemainingNFITBytes = pNFit->Header.Length - sizeof(*pNFit); CHECK_RESULT_MALLOC(*ppParsedNfit, (ParsedFitHeader *)AllocateZeroPool(sizeof(**ppParsedNfit)), Finish); pParsedNfit = *ppParsedNfit; CHECK_RESULT_MALLOC(pParsedNfit->pFit, (NFitHeader *)AllocateZeroPool(sizeof(*(pParsedNfit->pFit))), Finish); CopyMem_S(pParsedNfit->pFit, sizeof(*(pParsedNfit->pFit)), pNFit, sizeof(*(pParsedNfit->pFit))); while (RemainingNFITBytes > 0) { if (pTableHeader->Length == 0) { NVDIMM_DBG("Zero size entry found in nfit region."); goto Finish; } RemainingNFITBytes -= pTableHeader->Length; switch(pTableHeader->Type) { case NVDIMM_SPA_RANGE_TYPE: CHECK_RESULT_MALLOC(pParsedNfit->ppSpaRangeTbles, (SpaRangeTbl **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedNfit->ppSpaRangeTbles, pTabPointer, pTableHeader->Length, &pParsedNfit->SpaRangeTblesNum), Finish); break; case NVDIMM_NVDIMM_REGION_TYPE: CHECK_RESULT_MALLOC(pParsedNfit->ppNvDimmRegionMappingStructures, (NvDimmRegionMappingStructure **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedNfit->ppNvDimmRegionMappingStructures, pTabPointer, pTableHeader->Length, &pParsedNfit->NvDimmRegionMappingStructuresNum), Finish); break; case NVDIMM_INTERLEAVE_TYPE: CHECK_RESULT_MALLOC(pParsedNfit->ppInterleaveTbles, (InterleaveStruct **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedNfit->ppInterleaveTbles, pTabPointer, pTableHeader->Length, &pParsedNfit->InterleaveTblesNum), Finish); break; case NVDIMM_SMBIOS_MGMT_INFO_TYPE: CHECK_RESULT_MALLOC(pParsedNfit->ppSmbiosTbles, (SmbiosTbl **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedNfit->ppSmbiosTbles, pTabPointer, pTableHeader->Length, &pParsedNfit->SmbiosTblesNum), Finish); break; case NVDIMM_CONTROL_REGION_TYPE: CHECK_RESULT_MALLOC(pParsedNfit->ppControlRegionTbles, (ControlRegionTbl **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedNfit->ppControlRegionTbles, pTabPointer, pTableHeader->Length, &pParsedNfit->ControlRegionTblesNum), Finish); break; case NVDIMM_BW_DATA_WINDOW_REGION_TYPE: CHECK_RESULT_MALLOC(pParsedNfit->ppBWRegionTbles, (BWRegionTbl **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedNfit->ppBWRegionTbles, pTabPointer, pTableHeader->Length, &pParsedNfit->BWRegionTblesNum), Finish); break; case NVDIMM_FLUSH_HINT_TYPE: CHECK_RESULT_MALLOC(pParsedNfit->ppFlushHintTbles, (FlushHintTbl **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedNfit->ppFlushHintTbles, pTabPointer, pTableHeader->Length, &pParsedNfit->FlushHintTblesNum), Finish); break; case NVDIMM_PLATFORM_CAPABILITIES_TYPE: CHECK_RESULT_MALLOC(pParsedNfit->ppPlatformCapabilitiesTbles, (PlatformCapabilitiesTbl **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedNfit->ppPlatformCapabilitiesTbles, pTabPointer, pTableHeader->Length, &pParsedNfit->PlatformCapabilitiesTblesNum), Finish); break; default: break; } pTabPointer += pTableHeader->Length; pTableHeader = (SubTableHeader *)pTabPointer; } ReturnCode = EFI_SUCCESS; Finish: if (EFI_ERROR(ReturnCode) && ppParsedNfit != NULL && *ppParsedNfit != NULL) { FreeParsedNfit(ppParsedNfit); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Performs deserialization from binary memory block, containing PCAT tables, into parsed structure of pointers. @param[in] pTable pointer to the memory containing the PCAT binary representation. @param[out] ppParsedPcat Pointer to a pointer where the allocated and parsed PCAT table will be stored @retval EFI_INVALID_PARAMETER One of the provided parameters is invalid @retval EFI_VOLUME_CORRUPTED If the table checksum is invalid @retval EFI_INCOMPATIBLE_VERSION If the table is not compatible with this ipmctl version @retval EFI_SUCCESS **/ EFI_STATUS ParsePcatTable ( IN VOID *pTable, OUT ParsedPcatHeader **ppParsedPcat ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; ParsedPcatHeader *pParsedPcat = NULL; //!< Output Parsed PCAT structures PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPcatHeader = NULL; //!< PCAT header PCAT_TABLE_HEADER *pPcatSubTableHeader = NULL; //!< PCAT subtable header UINT32 RemainingPcatBytes = 0; UINT32 Length = 0; NVDIMM_ENTRY(); CHECK_NULL_ARG(pTable, Finish); CHECK_NULL_ARG(ppParsedPcat, Finish); pPcatHeader = (PLATFORM_CONFIG_ATTRIBUTES_TABLE *) pTable; if (!IsChecksumValid(pPcatHeader, pPcatHeader->Header.Length, pPcatHeader->Header.Checksum)) { NVDIMM_DBG("The checksum of PCAT table is invalid."); ReturnCode = EFI_VOLUME_CORRUPTED; goto Finish; } if (IS_PCAT_REVISION_INVALID(pPcatHeader->Header.Revision)) { NVDIMM_DBG("PCAT table revision is invalid"); ReturnCode = EFI_INCOMPATIBLE_VERSION; goto Finish; } pPcatSubTableHeader = (PCAT_TABLE_HEADER *) &pPcatHeader->pPcatTables; RemainingPcatBytes = pPcatHeader->Header.Length - sizeof(*pPcatHeader); CHECK_RESULT_MALLOC(*ppParsedPcat, (ParsedPcatHeader *)AllocateZeroPool(sizeof(*pParsedPcat)), Finish); pParsedPcat = *ppParsedPcat; CHECK_RESULT_MALLOC(pParsedPcat->pPlatformConfigAttr, (PLATFORM_CONFIG_ATTRIBUTES_TABLE *)AllocateZeroPool( sizeof(*pParsedPcat->pPlatformConfigAttr)), Finish); // Copying PCAT header to parsed structure CopyMem_S(pParsedPcat->pPlatformConfigAttr, sizeof(*pParsedPcat->pPlatformConfigAttr), pPcatHeader, sizeof(*pParsedPcat->pPlatformConfigAttr)); // Looking for sub tables while (RemainingPcatBytes > 0) { Length = pPcatSubTableHeader->Length; if (Length == 0) { NVDIMM_DBG("Length can't be 0."); goto Finish; } switch(pPcatSubTableHeader->Type) { case PCAT_TYPE_PLATFORM_CAPABILITY_INFO_TABLE: if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pPcatHeader)) { CHECK_RESULT_MALLOC(pParsedPcat->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo, (PLATFORM_CAPABILITY_INFO **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedPcat->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo, pPcatSubTableHeader, Length, &pParsedPcat->PlatformCapabilityInfoNum), Finish); } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPcatHeader)) { CHECK_RESULT_MALLOC(pParsedPcat->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo, (PLATFORM_CAPABILITY_INFO3 **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedPcat->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo, pPcatSubTableHeader, Length, &pParsedPcat->PlatformCapabilityInfoNum), Finish); if (IS_ACPI_HEADER_REV_MAJ_1_MIN_1_OR_2(pPcatHeader) || IS_ACPI_HEADER_REV_MAJ_3_MIN_1(pPcatHeader)) { // Backwards compatibility. Platforms with PCAT revisions 1.1, 1.2 & 3.1 always support mixed mode pParsedPcat->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[pParsedPcat->PlatformCapabilityInfoNum - 1]-> MemoryModeCapabilities.MemoryModesFlags.MixedMode = MIXED_MODE_CAPABILITY_SUPPORTED; } } break; case PCAT_TYPE_INTERLEAVE_CAPABILITY_INFO_TABLE: if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pPcatHeader)) { CHECK_RESULT_MALLOC(pParsedPcat->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo, (MEMORY_INTERLEAVE_CAPABILITY_INFO **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedPcat->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo, pPcatSubTableHeader, Length, &pParsedPcat->MemoryInterleaveCapabilityInfoNum), Finish); } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPcatHeader)) { CHECK_RESULT_MALLOC(pParsedPcat->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo, (MEMORY_INTERLEAVE_CAPABILITY_INFO3 **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedPcat->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo, pPcatSubTableHeader, Length, &pParsedPcat->MemoryInterleaveCapabilityInfoNum), Finish); } break; case PCAT_TYPE_RUNTIME_INTERFACE_TABLE: CHECK_RESULT_MALLOC(pParsedPcat->ppRuntimeInterfaceValConfInput, (RECONFIGURATION_INPUT_VALIDATION_INTERFACE_TABLE **) CopyMemoryAndAddPointerToArray( (VOID **) pParsedPcat->ppRuntimeInterfaceValConfInput, pPcatSubTableHeader, Length, &pParsedPcat->RuntimeInterfaceValConfInputNum), Finish); break; case PCAT_TYPE_CONFIG_MANAGEMENT_ATTRIBUTES_TABLE: CHECK_RESULT_MALLOC(pParsedPcat->ppConfigManagementAttributesInfo, (CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE **) CopyMemoryAndAddPointerToArray( (VOID **) pParsedPcat->ppConfigManagementAttributesInfo, pPcatSubTableHeader, Length, &pParsedPcat->ConfigManagementAttributesInfoNum), Finish); break; case PCAT_TYPE_SOCKET_SKU_INFO_TABLE: if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pPcatHeader)) { CHECK_RESULT_MALLOC(pParsedPcat->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable, (SOCKET_SKU_INFO_TABLE **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedPcat->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable, pPcatSubTableHeader, Length, &pParsedPcat->SocketSkuInfoNum), Finish); } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPcatHeader)) { CHECK_RESULT_MALLOC(pParsedPcat->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable, (DIE_SKU_INFO_TABLE **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedPcat->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable, pPcatSubTableHeader, Length, &pParsedPcat->SocketSkuInfoNum), Finish); } break; default: NVDIMM_WARN("Unknown type of PCAT table."); goto Finish; } RemainingPcatBytes -= Length; pPcatSubTableHeader = (PCAT_TABLE_HEADER *) ((UINT8 *)pPcatSubTableHeader + Length); } ReturnCode = EFI_SUCCESS; Finish: if (EFI_ERROR(ReturnCode) && NULL != ppParsedPcat && NULL != *ppParsedPcat) { FreeParsedPcat(ppParsedPcat); } NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Performs deserialization from binary memory block, containing PMTT tables, into parsed structure of pointers. @param[in] pTable pointer to the memory containing the PMTT binary representation. @param[out] ppParsedPmtt Pointer to a pointer where the allocated and parsed PMTT table will be stored @retval EFI_INVALID_PARAMETER One of the provided parameters is invalid @retval EFI_VOLUME_CORRUPTED If the table checksum is invalid @retval EFI_INCOMPATIBLE_VERSION If the table is not compatible with this ipmctl version @retval EFI_SUCCESS **/ EFI_STATUS ParsePmttTable( IN VOID *pTable, OUT ParsedPmttHeader **ppParsedPmtt ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; ParsedPmttHeader *pParsedPmtt = NULL; //!< Output Parsed PMTT structures PMTT_TABLE2 *pPmttHeader = NULL; //!< PMTT header PMTT_COMMON_HEADER2 *pPmttCommonTableHeader = NULL; //!< PMTT common header PMTT_MODULE_INFO *pModuleInfo = NULL; DIMM_INFO* pDimmInfo = NULL; UINT32 RemainingPmttBytes = 0; UINT32 Length = 0; UINT16 SocketID = 0; UINT16 DieID = 0; UINT16 CpuID = 0; UINT16 iMCID = 0; UINT16 ChannelID = 0; UINT16 SlotID = 0; UINT32 NumOfMemoryDevices = 0; UINT32 DieLevelNumOfMemoryDevices = 0; UINT32 TotalDiesNum = 0; UINT32 TotaliMCsNum = 0; UINT32 TotalChannelsNum = 0; NVDIMM_ENTRY(); CHECK_NULL_ARG(pTable, Finish); CHECK_NULL_ARG(ppParsedPmtt, Finish); // Prepare for error scenario first *ppParsedPmtt = NULL; pPmttHeader = (PMTT_TABLE2 *)pTable; if (!IsChecksumValid(pPmttHeader, pPmttHeader->Header.Length, pPmttHeader->Header.Checksum)) { NVDIMM_DBG("The checksum of PMTT table is invalid."); ReturnCode = EFI_VOLUME_CORRUPTED; goto Finish; } /** Parse the PMTT Rev 0.2 table only ACPI 6.4 requires DIMM fields to be populated using PMTT if NfitDeviceHandle Bit 31 is set **/ if (IS_PMTT_REVISION_INVALID(pPmttHeader->Header.Revision)) { // PMTT != 0.1 and PMTT != 0.2 NVDIMM_DBG("PMTT table revision is invalid"); ReturnCode = EFI_INCOMPATIBLE_VERSION; goto Finish; } else if (IS_ACPI_REV_MAJ_0_MIN_1(pPmttHeader->Header.Revision)) { NVDIMM_DBG("Choosing to not parse PMTT table right now, will parse later as needed"); ReturnCode = EFI_SUCCESS; goto Finish; } // PMTT == 0.2 pPmttCommonTableHeader = (PMTT_COMMON_HEADER2 *)&pPmttHeader->pPmttDevices; RemainingPmttBytes = pPmttHeader->Header.Length - sizeof(*pPmttHeader); CHECK_RESULT_MALLOC(*ppParsedPmtt, (ParsedPmttHeader *)AllocateZeroPool(sizeof(*pParsedPmtt)), Finish); pParsedPmtt = *ppParsedPmtt; CHECK_RESULT_MALLOC(pParsedPmtt->pPmtt, (PMTT_TABLE2 *)AllocateZeroPool(sizeof(*pParsedPmtt->pPmtt)), Finish); // Copying PMTT header to parsed structure CopyMem_S(pParsedPmtt->pPmtt, sizeof(*pParsedPmtt->pPmtt), pPmttHeader, sizeof(*pParsedPmtt->pPmtt)); // Looking for sub tables while (RemainingPmttBytes > 0) { Length = pPmttCommonTableHeader->Length; if (Length == 0) { NVDIMM_DBG("Length of PMTT common header is zero."); goto Finish; } /** Calculate the total number of Dies, iMCs & channels on the platform including the disabled ones. Disabled here means there are no PMems or DDRs populated under it. **/ if (pPmttCommonTableHeader->Type == PMTT_TYPE_iMC) { TotaliMCsNum++; } else if (CompareMem(&((PMTT_VENDOR_SPECIFIC2 *)((UINT8 *)pPmttCommonTableHeader))->TypeUUID, &gDieTypeDeviceGuid, sizeof(gDieTypeDeviceGuid)) == 0) { TotalDiesNum++; } else if (CompareMem(&((PMTT_VENDOR_SPECIFIC2 *)((UINT8 *)pPmttCommonTableHeader))->TypeUUID, &gChannelTypeDeviceGuid, sizeof(gChannelTypeDeviceGuid)) == 0) { TotalChannelsNum++; } // Skip the devices which are not part of the physical topology or are disabled if (!(pPmttCommonTableHeader->Flags & PMTT_PHYSICAL_ELEMENT_OF_TOPOLOGY)) { NVDIMM_DBG("Not a physical element of the topology!"); RemainingPmttBytes -= Length; pPmttCommonTableHeader = (PMTT_COMMON_HEADER2 *)((UINT8 *)pPmttCommonTableHeader + Length); continue; } switch (pPmttCommonTableHeader->Type) { case PMTT_TYPE_SOCKET: { CHECK_RESULT_MALLOC(pParsedPmtt->ppSockets, (PMTT_SOCKET2 **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedPmtt->ppSockets, pPmttCommonTableHeader, Length, &pParsedPmtt->SocketsNum), Finish); SocketID = pParsedPmtt->ppSockets[pParsedPmtt->SocketsNum - 1]->SocketId; DieLevelNumOfMemoryDevices += NumOfMemoryDevices; NumOfMemoryDevices = pParsedPmtt->ppSockets[pParsedPmtt->SocketsNum - 1]->Header.NoOfMemoryDevices; DieID = MAX_DIEID_SINGLE_DIE_SOCKET; break; } case PMTT_TYPE_VENDOR_SPECIFIC: { PMTT_VENDOR_SPECIFIC2 *pVendorDevice = (PMTT_VENDOR_SPECIFIC2 *)((UINT8 *)pPmttCommonTableHeader); if (CompareMem(&pVendorDevice->TypeUUID, &gDieTypeDeviceGuid, sizeof(pVendorDevice->TypeUUID)) == 0) { CHECK_RESULT_MALLOC(pParsedPmtt->ppDies, (PMTT_VENDOR_SPECIFIC2 **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedPmtt->ppDies, pPmttCommonTableHeader, Length, &pParsedPmtt->DiesNum), Finish); DieID = pParsedPmtt->ppDies[pParsedPmtt->DiesNum - 1]->DeviceID; CpuID = (DieLevelNumOfMemoryDevices & MAX_UINT16) + DieID; } else if (CompareMem(&pVendorDevice->TypeUUID, &gChannelTypeDeviceGuid, sizeof(pVendorDevice->TypeUUID)) == 0) { CHECK_RESULT_MALLOC(pParsedPmtt->ppChannels, (PMTT_VENDOR_SPECIFIC2 **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedPmtt->ppChannels, pPmttCommonTableHeader, Length, &pParsedPmtt->ChannelsNum), Finish); ChannelID = pParsedPmtt->ppChannels[pParsedPmtt->ChannelsNum - 1]->DeviceID; SlotID = 0; } else if (CompareMem(&pVendorDevice->TypeUUID, &gSlotTypeDeviceGuid, sizeof(pVendorDevice->TypeUUID)) == 0) { CHECK_RESULT_MALLOC(pParsedPmtt->ppSlots, (PMTT_VENDOR_SPECIFIC2 **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedPmtt->ppSlots, pPmttCommonTableHeader, Length, &pParsedPmtt->SlotsNum), Finish); SlotID = pParsedPmtt->ppSlots[pParsedPmtt->SlotsNum - 1]->DeviceID; } else { NVDIMM_DBG("Unknown PMTT Vendor Specific Data"); continue; } break; } case PMTT_TYPE_iMC: CHECK_RESULT_MALLOC(pParsedPmtt->ppiMCs, (PMTT_iMC2 **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedPmtt->ppiMCs, pPmttCommonTableHeader, Length, &pParsedPmtt->iMCsNum), Finish); iMCID = pParsedPmtt->ppiMCs[pParsedPmtt->iMCsNum - 1]->MemControllerID; ChannelID = 0; break; case PMTT_TYPE_MODULE: { PMTT_MODULE2 *pModule = (PMTT_MODULE2 *)pPmttCommonTableHeader; // skip if Bits [3:2] are reserved if ((pPmttCommonTableHeader->Flags & PMTT_TYPE_RESERVED) == PMTT_TYPE_RESERVED) { NVDIMM_DBG("Reserved. No indication in PMTT if this module is volatile or non-volatile memory!"); break; } pModuleInfo = (PMTT_MODULE_INFO *)AllocateZeroPool(sizeof(*pModuleInfo)); if (pModuleInfo == NULL) { NVDIMM_DBG("Memory allocation error."); goto Finish; } pModuleInfo->Header = pModule->Header; pModuleInfo->SmbiosHandle = pModule->SmbiosHandle & SMBIOS_HANDLE_MASK; pModuleInfo->SocketId = SocketID; pModuleInfo->DieId = DieID; pModuleInfo->CpuId = CpuID; pModuleInfo->MemControllerId = iMCID; pModuleInfo->ChannelId = ChannelID; pModuleInfo->SlotId = SlotID; // BIT 2 is set then PMem module or else DDR type if (pPmttCommonTableHeader->Flags & PMTT_DDR_DCPM_FLAG) { pModuleInfo->MemoryType = MEMORYTYPE_DCPM; CHECK_RESULT_MALLOC(pParsedPmtt->ppDCPMModules, (PMTT_MODULE_INFO **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedPmtt->ppDCPMModules, pModuleInfo, sizeof(*pModuleInfo), &pParsedPmtt->DCPMModulesNum), Finish); } else { pDimmInfo = (DIMM_INFO*)AllocateZeroPool(sizeof(*pDimmInfo)); if (pDimmInfo == NULL) { NVDIMM_WARN("Memory allocation error"); goto Finish; } pDimmInfo->DimmID = pModuleInfo->SmbiosHandle; if (EFI_ERROR(FillSmbiosInfo(pDimmInfo))) { NVDIMM_DBG("Smbios information could not be retrieved."); goto Finish; } pModuleInfo->MemoryType = pDimmInfo->MemoryType; CHECK_RESULT_MALLOC(pParsedPmtt->ppDDRModules, (PMTT_MODULE_INFO **)CopyMemoryAndAddPointerToArray( (VOID **)pParsedPmtt->ppDDRModules, pModuleInfo, sizeof(*pModuleInfo), &pParsedPmtt->DDRModulesNum), Finish); FREE_POOL_SAFE(pDimmInfo); } FREE_POOL_SAFE(pModuleInfo); break; } default: NVDIMM_WARN("Unknown type of PMTT table."); goto Finish; } RemainingPmttBytes -= Length; pPmttCommonTableHeader = (PMTT_COMMON_HEADER2 *)((UINT8 *)pPmttCommonTableHeader + Length); } if (TotalDiesNum > 0 && TotaliMCsNum > 0) { if (((TotaliMCsNum % TotalDiesNum) != 0) || ((TotalChannelsNum % TotaliMCsNum) != 0)) { NVDIMM_ERR("Topology inconsistent in PMTT table."); goto Finish; } pParsedPmtt->iMCsNumPerDie = TotaliMCsNum / TotalDiesNum; pParsedPmtt->ChannelsNumPeriMC = TotalChannelsNum / TotaliMCsNum; } ReturnCode = EFI_SUCCESS; Finish: if (EFI_ERROR(ReturnCode) && NULL != ppParsedPmtt && NULL != *ppParsedPmtt) { FreeParsedPmtt(ppParsedPmtt); } FREE_POOL_SAFE(pDimmInfo); FREE_POOL_SAFE(pModuleInfo); return ReturnCode; } /** Get PMTT Dimm Module by Dimm ID Scan the dimm list for a dimm identified by Dimm ID @param[in] DimmID: The SMBIOS Type 17 handle of the dimm @param[in] pPmttHead: Parsed PMTT Table @retval PMTT_MODULE_INFO struct pointer if matching dimm has been found @retval NULL pointer if not found **/ PMTT_MODULE_INFO * GetDimmModuleByPidFromPmtt( IN UINT32 DimmID, IN ParsedPmttHeader *pPmttHead ) { UINT32 Index = 0; PMTT_MODULE_INFO *pModuleInfo = NULL; NVDIMM_ENTRY(); if (pPmttHead == NULL) { NVDIMM_DBG("PMTT Table header NULL"); goto Finish; } for (Index = 0; Index < pPmttHead->DCPMModulesNum; Index++) { if (pPmttHead->ppDCPMModules[Index]->SmbiosHandle == DimmID) { pModuleInfo = pPmttHead->ppDCPMModules[Index]; } } Finish: NVDIMM_EXIT(); return pModuleInfo; } /** Retrieve the Logical Socket ID from PMTT Table @param[in] SocketId SocketID @param[in] DieId DieID @param[out] pLogicalSocketId Logical socket ID based on Dimm socket ID & Die ID @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_NOT_FOUND PCAT socket sku info table not found for given socketID **/ EFI_STATUS GetLogicalSocketIdFromPmtt( IN UINT32 SocketId, IN UINT32 DieId, OUT UINT32 *pLogicalSocketId ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; TABLE_HEADER *pTable = NULL; UINT32 Index = 0; UINT32 NoOfMemoryDevices = 0; BOOLEAN Found = FALSE; ParsedPmttHeader *pPmttHead = NULL; NVDIMM_ENTRY(); if (pLogicalSocketId == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Only PMTT >= 0.2 are parsed. Anything else is NULL pPmttHead = gNvmDimmData->PMEMDev.pPmttHead; if (pPmttHead == NULL) { // If the parsed PMTT table is missing, we are on a Purley platform and the logical // socket is the same as a physical socket. No changes needed *pLogicalSocketId = SocketId; ReturnCode = EFI_SUCCESS; goto Finish; } for (Index = 0; Index < pPmttHead->SocketsNum; Index++) { if (SocketId == pPmttHead->ppSockets[Index]->SocketId) { Found = TRUE; break; } NoOfMemoryDevices += pPmttHead->ppSockets[Index]->Header.NoOfMemoryDevices; } if (!Found) { NVDIMM_DBG("Socket ID not found"); ReturnCode = EFI_NOT_FOUND; goto Finish; } // Searching for matching Die ID // Reset Found to FALSE again (from TRUE) Found = FALSE; for (Index = 0; Index < pPmttHead->DiesNum; Index++) { if (DieId == pPmttHead->ppDies[Index]->DeviceID) { *pLogicalSocketId = NoOfMemoryDevices + DieId; Found = TRUE; break; } } if (!Found) { NVDIMM_DBG("Die ID not found"); ReturnCode = EFI_NOT_FOUND; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pTable); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check if the current population is a special non-por config supported when cross-tiling is enabled @param[out] pNonPorCrossTileSupportedConfig pointer to non-por config supported boolean flag @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pNonPorCrossTileSupportedConfig is NULL @retval EFI_NOT_FOUND Parsed PMTT table is NULL **/ EFI_STATUS CheckIsNonPorCrossTileSupportedConfig( OUT BOOLEAN *pNonPorCrossTileSupportedConfig ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; ParsedPmttHeader *pPmttHead = NULL; BOOLEAN CrossTileCachingSupported = FALSE; NVDIMM_ENTRY(); if (pNonPorCrossTileSupportedConfig == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Default value is false, this feature isn't supported *pNonPorCrossTileSupportedConfig = FALSE; ReturnCode = CheckIsCrossTileCachingSupported(&CrossTileCachingSupported); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to determine if cross-tile caching supported."); goto Finish; } if (!CrossTileCachingSupported) { goto Finish; } pPmttHead = gNvmDimmData->PMEMDev.pPmttHead; if (NULL == pPmttHead) { NVDIMM_DBG("Pmtt head not found."); // This feature is not supported on older platforms (NULL PMTT or 0.1 PMTT) // Leave the supported field as false and return success ReturnCode = EFI_SUCCESS; goto Finish; } /** Manufacturing 1+1 internal only non-por config BIOS allows this config only when BIOS knob for Enforce Population POR is disabled and cross-tiling caching is supported **/ if ((pPmttHead->DDRModulesNum == 1) && (pPmttHead->DCPMModulesNum == 1) && (pPmttHead->ppDDRModules[0]->MemControllerId != pPmttHead->ppDCPMModules[0]->MemControllerId)) { *pNonPorCrossTileSupportedConfig = TRUE; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve the platform topology information (iMCs per die, Channels per iMc) @param[out] piMCsNumPerDie Pointer to number of iMCs per die @param[out] pChannelsNumPeriMC Pointer to number of channels per iMC @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_NOT_FOUND Parsed PMTT table pointer is NULL **/ EFI_STATUS RetrievePlatformTopologyFromPmtt( OUT UINT32 *piMCsNumPerDie, OUT UINT32 *pChannelsNumPeriMC ) { EFI_STATUS ReturnCode = EFI_SUCCESS; ParsedPmttHeader *pPmttHead = NULL; NVDIMM_ENTRY(); CHECK_NULL_ARG(piMCsNumPerDie, Finish); CHECK_NULL_ARG(pChannelsNumPeriMC, Finish); // Default to Purley configuration *piMCsNumPerDie = IMCS_PER_DIE_2_3; *pChannelsNumPeriMC = CHANNELS_PER_IMC_2_3; // Use the pre-parsed PMTT table (0.2 only, from driver init) for determining the true value // since we don't need to parse everything again pPmttHead = gNvmDimmData->PMEMDev.pPmttHead; if (NULL == pPmttHead) { ReturnCode = EFI_SUCCESS; NVDIMM_DBG("On Purley platform w/ either valid or missing PMTT, using Purley topology"); goto Finish; } *piMCsNumPerDie = pPmttHead->iMCsNumPerDie; *pChannelsNumPeriMC = pPmttHead->ChannelsNumPeriMC; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Performs deserialization from binary memory block containing PMTT table and checks if memory mode can be configured. @param[in] pTable pointer to the memory containing the PMTT binary representation. @retval false if topology does NOT allows MM. @retval true if topology allows MM. **/ BOOLEAN CheckIsMemoryModeAllowed( IN TABLE_HEADER *pTable ) { EFI_STATUS ReturnCode = EFI_SUCCESS; BOOLEAN MMCanBeConfigured = FALSE; BOOLEAN IsDDR = FALSE; BOOLEAN IsDCPM = FALSE; if (pTable == NULL) { goto Finish; } if (!IsChecksumValid(pTable, pTable->Length, pTable->Checksum)) { NVDIMM_WARN("The checksum of PMTT table is invalid."); goto Finish; } if (IS_ACPI_REV_MAJ_0_MIN_1(pTable->Revision)) { PMTT_TABLE *pPMTT = (PMTT_TABLE *)pTable; UINT64 Offset = sizeof(pPMTT->Header) + sizeof(pPMTT->Reserved); PMTT_COMMON_HEADER *pCommonHeader = (PMTT_COMMON_HEADER *)(((UINT8 *)pPMTT) + Offset); while (Offset < pPMTT->Header.Length && pCommonHeader->Type == PMTT_TYPE_SOCKET) { // check if socket is enabled if (pCommonHeader->Flags) { Offset += sizeof(PMTT_SOCKET) + PMTT_COMMON_HDR_LEN; pCommonHeader = (PMTT_COMMON_HEADER *)(((UINT8 *)pPMTT) + Offset); while (Offset < pPMTT->Header.Length && pCommonHeader->Type == PMTT_TYPE_iMC) { // check if iMC is enabled if (pCommonHeader->Flags) { Offset += sizeof(PMTT_iMC) + PMTT_COMMON_HDR_LEN; pCommonHeader = (PMTT_COMMON_HEADER *)(((UINT8 *)pPMTT) + Offset); // check if at least one DCPMM is present while (Offset < pPMTT->Header.Length && pCommonHeader->Type == PMTT_TYPE_MODULE) { PMTT_MODULE *pModule = (PMTT_MODULE *)(((UINT8 *)pCommonHeader) + sizeof(pCommonHeader)); // if IsDCPM is already set then continue to loop to find the offset of the next aggregated device if (!IsDCPM) { // bit 2 is set then DCPMM if ((pCommonHeader->Flags & PMTT_DDR_DCPM_FLAG) && pModule->SizeOfDimm > 0) { IsDCPM = TRUE; } else if (!(pCommonHeader->Flags & PMTT_DDR_DCPM_FLAG) && pModule->SizeOfDimm > 0) { IsDDR = TRUE; } } Offset += sizeof(PMTT_MODULE) + PMTT_COMMON_HDR_LEN; pCommonHeader = (PMTT_COMMON_HEADER *)(((UINT8 *)pPMTT) + Offset); } // end of Module if (IsDDR && !IsDCPM) { MMCanBeConfigured = FALSE; goto Finish; } MMCanBeConfigured = TRUE; IsDDR = FALSE; IsDCPM = FALSE; } else { // iMC is disabled Offset += pCommonHeader->Length; pCommonHeader = (PMTT_COMMON_HEADER *)(((UINT8 *)pPMTT) + Offset); } } // end of iMC } else { // socket is disabled Offset += pCommonHeader->Length; pCommonHeader = (PMTT_COMMON_HEADER *)(((UINT8 *)pPMTT) + Offset); } } // end of socket } else if (IS_ACPI_REV_MAJ_0_MIN_2(pTable->Revision)) { PMTT_TABLE2 *pPMTT = NULL; UINT32 Index1 = 0; UINT32 Index2 = 0; BOOLEAN CrossTileCachingSupported = FALSE; BOOLEAN NonPorCrossTileSupportedConfig = FALSE; if (gNvmDimmData->PMEMDev.pPmttHead == NULL || gNvmDimmData->PMEMDev.pPmttHead->iMCsNum == 0) { NVDIMM_DBG("Incorrect PMTT table"); goto Finish; } ReturnCode = CheckIsCrossTileCachingSupported(&CrossTileCachingSupported); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to determine if cross-tile caching supported."); goto Finish; } ReturnCode = CheckIsNonPorCrossTileSupportedConfig(&NonPorCrossTileSupportedConfig); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CheckIsNonPorCrossTileSupportedConfig failed."); goto Finish; } /** Manufacturing 1+1 internal only non-por config BIOS allows this config only when BIOS knob for Enforce Population POR is disabled and cross-tiling caching is supported **/ if (NonPorCrossTileSupportedConfig) { MMCanBeConfigured = TRUE; goto Finish; } pPMTT = gNvmDimmData->PMEMDev.pPmttHead->pPmtt; if (IS_ACPI_HEADER_REV_MAJ_0_MIN_2(pPMTT)) { for (Index1 = 0; Index1 < gNvmDimmData->PMEMDev.pPmttHead->DDRModulesNum; Index1++) { for (Index2 = 0; Index2 < gNvmDimmData->PMEMDev.pPmttHead->DCPMModulesNum; Index2++) { if (gNvmDimmData->PMEMDev.pPmttHead->ppDDRModules[Index1]->SocketId == gNvmDimmData->PMEMDev.pPmttHead->ppDCPMModules[Index2]->SocketId && gNvmDimmData->PMEMDev.pPmttHead->ppDDRModules[Index1]->DieId == gNvmDimmData->PMEMDev.pPmttHead->ppDCPMModules[Index2]->DieId && gNvmDimmData->PMEMDev.pPmttHead->ppDDRModules[Index1]->MemControllerId == gNvmDimmData->PMEMDev.pPmttHead->ppDCPMModules[Index2]->MemControllerId) { IsDCPM = TRUE; break; } } if (CrossTileCachingSupported) { if (IsDCPM) { MMCanBeConfigured = TRUE; goto Finish; } } else { if (!IsDCPM) { MMCanBeConfigured = FALSE; goto Finish; } MMCanBeConfigured = TRUE; } IsDCPM = FALSE; } } } Finish: return MMCanBeConfigured; } /** Returns the FlushHint table associated with the provided NVDIMM region table. @param[in] pFitHead pointer to the parsed NFit Header structure. @param[in] pNvDimmRegionMappingStructure the NVDIMM region table that contains the index. @param[out] ppFlushHintTable pointer to a pointer where the table will be stored. @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. @retval EFI_NOT_FOUND if there is no Interleave table with the provided index. **/ EFI_STATUS GetFlushHintTableForNvDimmRegionTable( IN ParsedFitHeader *pFitHead, IN NvDimmRegionMappingStructure *pNvDimmRegionMappingStructure, OUT FlushHintTbl **ppFlushHintTable ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; UINT32 Index = 0; if (pFitHead == NULL || pNvDimmRegionMappingStructure == NULL || ppFlushHintTable == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } for (Index = 0; Index < pFitHead->FlushHintTblesNum; Index++) { if (pNvDimmRegionMappingStructure->DeviceHandle.AsUint32 == pFitHead->ppFlushHintTbles[Index]->DeviceHandle.AsUint32) { *ppFlushHintTable = pFitHead->ppFlushHintTbles[Index]; ReturnCode = EFI_SUCCESS; } } Finish: return ReturnCode; } /** GetBlockDataWindowRegDescTabl - returns the Block Data Window Table associated with the provided Control Region Table. @param[in] pFitHead pointer to the parsed NFit Header structure. @param[in] pControlRegionTable the Control Region table that contains the index. @param[out] ppBlockDataWindowTable pointer to a pointer where the table will be stored. @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if pFitHead or ControlRegionTbl or BWRegionTbl equals NULL. @retval EFI_NOT_FOUND if there is no Block Data Window Descriptor table with the provided index. **/ EFI_STATUS GetBlockDataWindowRegDescTabl( IN ParsedFitHeader *pFitHead, IN ControlRegionTbl *pControlRegTbl, OUT BWRegionTbl **ppBlockDataWindowTable ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; UINT16 Index = 0; UINT16 ControlTableIndex = 0; if (pFitHead == NULL || pControlRegTbl == NULL || ppBlockDataWindowTable == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ControlTableIndex = pControlRegTbl->ControlRegionDescriptorTableIndex; for (Index = 0; Index < pFitHead->BWRegionTblesNum; Index++) { if (pFitHead->ppBWRegionTbles[Index]->ControlRegionStructureIndex == ControlTableIndex) { *ppBlockDataWindowTable = pFitHead->ppBWRegionTbles[Index]; ReturnCode = EFI_SUCCESS; break; } } Finish: return ReturnCode; } /** Returns the ControlRegion table associated with the provided NVDIMM region table. @param[in] pFitHead pointer to the parsed NFit Header structure. @param[in] pNvDimmRegionMappingStructure the NVDIMM region table that contains the index. @param[out] ppControlRegionTable pointer to a pointer where the table will be stored. @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more input parameters equal NULL. @retval EFI_NOT_FOUND if there is no Control Region table with the provided index. **/ EFI_STATUS GetControlRegionTableForNvDimmRegionTable( IN ParsedFitHeader *pFitHead, IN NvDimmRegionMappingStructure *pNvDimmRegionMappingStructure, OUT ControlRegionTbl **ppControlRegionTable ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; UINT16 Index = 0; UINT16 ControlTableIndex = 0; if (pFitHead == NULL || pNvDimmRegionMappingStructure == NULL || ppControlRegionTable == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *ppControlRegionTable = NULL; ControlTableIndex = pNvDimmRegionMappingStructure->NvdimmControlRegionDescriptorTableIndex; for (Index = 0; Index < pFitHead->ControlRegionTblesNum; Index++) { if (pFitHead->ppControlRegionTbles[Index]->ControlRegionDescriptorTableIndex == ControlTableIndex) { *ppControlRegionTable = pFitHead->ppControlRegionTbles[Index]; ReturnCode = EFI_SUCCESS; break; } } Finish: return ReturnCode; } /** Get Control Region table for provided PhysicalID @param[in] pFitHead pointer to the parsed NFit Header structure @param[in] Pid Dimm PhysicalID @param[out] pControlRegionTables array to store Control Region tables pointers @param[in, out] pControlRegionTablesNum size of array on input, number of items stored in the array on output @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_BUFFER_TOO_SMALL There is more Control Region tables in NFIT than size of provided array **/ EFI_STATUS GetControlRegionTablesForPID( IN ParsedFitHeader *pFitHead, IN UINT16 Pid, OUT ControlRegionTbl *pControlRegionTables[], IN OUT UINT32 *pControlRegionTablesNum ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; UINT32 Index2 = 0; UINT32 CurrentArrayNum = 0; ControlRegionTbl *pCtrlTable = NULL; BOOLEAN ContainedAlready = FALSE; NVDIMM_ENTRY(); if (pFitHead == NULL || pControlRegionTables == NULL || pControlRegionTablesNum == NULL) { goto Finish; } for (Index = 0; Index < pFitHead->NvDimmRegionMappingStructuresNum; Index++) { if (Pid == pFitHead->ppNvDimmRegionMappingStructures[Index]->NvDimmPhysicalId) { ReturnCode = GetControlRegionTableForNvDimmRegionTable( pFitHead, pFitHead->ppNvDimmRegionMappingStructures[Index], &pCtrlTable); /** Make sure the found Control Region table is not in the array already. **/ ContainedAlready = FALSE; for (Index2 = 0; Index2 < CurrentArrayNum; Index2++) { if (pCtrlTable == pControlRegionTables[Index2]) { ContainedAlready = TRUE; } } if (!ContainedAlready) { if (CurrentArrayNum >= *pControlRegionTablesNum) { NVDIMM_ERR("There are more Control Region tables than length of the input array."); ReturnCode = EFI_BUFFER_TOO_SMALL; goto Finish; } pControlRegionTables[CurrentArrayNum] = pCtrlTable; CurrentArrayNum++; } } } *pControlRegionTablesNum = CurrentArrayNum; ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** GetSpaRangeTable - returns the SpaRange Table with the provided Index. @param[in] pFitHead pointer to the parsed NFit Header structure. @param[in] SpaRangeTblIndex index of the table to be found. @param[out] ppSpaRangeTbl pointer to a pointer where the table will be stored. @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if pFitHead or ppInterleaveTbl equals NULL. @retval EFI_NOT_FOUND if there is no Interleave table with the provided index. **/ EFI_STATUS GetSpaRangeTable( IN ParsedFitHeader *pFitHead, IN UINT16 SpaRangeTblIndex, OUT SpaRangeTbl **ppSpaRangeTbl ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; UINT16 Index = 0; if (pFitHead == NULL || ppSpaRangeTbl == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *ppSpaRangeTbl = NULL; for (Index = 0; Index < pFitHead->SpaRangeTblesNum; Index++) { if (pFitHead->ppSpaRangeTbles[Index]->SpaRangeDescriptionTableIndex == SpaRangeTblIndex) { *ppSpaRangeTbl = pFitHead->ppSpaRangeTbles[Index]; ReturnCode = EFI_SUCCESS; break; } } Finish: return ReturnCode; } /** GetInterleaveTable - returns the Interleave Table with the provided Index. @param[in] pFitHead pointer to the parsed NFit Header structure. @param[in] InterleaveTblIndex index of the table to be found. @param[out] ppInterleaveTbl pointer to a pointer where the table will be stored. @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if pFitHead or ppInterleaveTbl equals NULL. @retval EFI_NOT_FOUND if there is no Interleave table with the provided index. **/ EFI_STATUS GetInterleaveTable( IN ParsedFitHeader *pFitHead, IN UINT16 InterleaveTblIndex, OUT InterleaveStruct **ppInterleaveTbl ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; UINT16 Index = 0; if (pFitHead == NULL || ppInterleaveTbl == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *ppInterleaveTbl = NULL; for (Index = 0; Index < pFitHead->InterleaveTblesNum; Index++) { if (pFitHead->ppInterleaveTbles[Index]->InterleaveStructureIndex == InterleaveTblIndex) { *ppInterleaveTbl = pFitHead->ppInterleaveTbles[Index]; ReturnCode = EFI_SUCCESS; break; } } Finish: return ReturnCode; } /** Finds in the provided Nfit structure the requested NVDIMM region. If the pAddrRangeTypeGuid equals NULL, the first table matching the Pid will be returned. @param[in] pFitHead pointer to the parsed NFit Header structure. @param[in] Pid the Dimm ID that the NVDIMM region must be for. @param[in] pAddrRangeTypeGuid pointer to GUID type of the range that we are looking for. OPTIONAL @param[in] SpaRangeIndexProvided Determine if SpaRangeIndex is provided @param[in] SpaRangeIndex Looking for NVDIMM region table that is related with provided SPA table. OPTIONAL @param[out] ppNvDimmRegionMappingStructure pointer to a pointer for the return NVDIMM region. @retval EFI_SUCCESS if the table was found and was returned. @retval EFI_INVALID_PARAMETER if one or more input parameters equal NULL. @retval EFI_NOT_FOUND if there is no NVDIMM region for the provided Dimm PID and AddrRangeType. **/ EFI_STATUS GetNvDimmRegionMappingStructureForPid( IN ParsedFitHeader *pFitHead, IN UINT16 Pid, IN GUID *pAddrRangeTypeGuid OPTIONAL, IN BOOLEAN SpaRangeIndexProvided, IN UINT16 SpaRangeIndex OPTIONAL, OUT NvDimmRegionMappingStructure **ppNvDimmRegionMappingStructure ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT16 Index = 0; SpaRangeTbl *pSpaRangeTbl = NULL; UINT16 SpaIndexInNvDimmRegion = 0; BOOLEAN Found = FALSE; if (pFitHead == NULL || Pid == DIMM_PID_ALL || Pid == DIMM_PID_INVALID || ppNvDimmRegionMappingStructure == NULL) { goto Finish; } *ppNvDimmRegionMappingStructure = NULL; for (Index = 0; Index < pFitHead->NvDimmRegionMappingStructuresNum; Index++) { if (pFitHead->ppNvDimmRegionMappingStructures[Index]->NvDimmPhysicalId != Pid) { continue; } SpaIndexInNvDimmRegion = pFitHead->ppNvDimmRegionMappingStructures[Index]->SpaRangeDescriptionTableIndex; Found = TRUE; if (SpaRangeIndexProvided && SpaIndexInNvDimmRegion != SpaRangeIndex) { Found = FALSE; } if (pAddrRangeTypeGuid != NULL) { pSpaRangeTbl = NULL; ReturnCode = GetSpaRangeTable(pFitHead, SpaIndexInNvDimmRegion, &pSpaRangeTbl); if (EFI_ERROR(ReturnCode) || pSpaRangeTbl == NULL || CompareMem(&pSpaRangeTbl->AddressRangeTypeGuid, pAddrRangeTypeGuid, sizeof(pSpaRangeTbl->AddressRangeTypeGuid)) != 0) { Found = FALSE; } } if (Found) { *ppNvDimmRegionMappingStructure = pFitHead->ppNvDimmRegionMappingStructures[Index]; ReturnCode = EFI_SUCCESS; break; } else { ReturnCode = EFI_NOT_FOUND; } } Finish: return ReturnCode; } /** RdpaToSpa() - Convert Device Region Physical to System Physical Address @param[in] Rdpa Device Region Physical Address to convert @param[in] pNvDimmRegionTable The NVDIMM region that helps describe this region of memory @param[in] pInterleaveTable Interleave table referenced by the MemDevToSpaRangeTable @param[out] SpaAddr output for SPA address A memory device could have multiple regions. As such we cannot convert to a device physical address. Instead we refer to the address for a region within the device as device region physical address (RDPA), where Rdpa is a zero based address from the start of the region within the device. @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER on a divide by zero error **/ EFI_STATUS RdpaToSpa( IN UINT64 Rdpa, IN NvDimmRegionMappingStructure *pNvDimmRegionTable, IN SpaRangeTbl *pSpaRangeTable, IN InterleaveStruct *pInterleaveTable OPTIONAL, OUT UINT64 *pSpaAddr ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT64 RotationSize = 0; UINT64 RotationNum = 0; UINT32 LineNum = 0; UINT64 StartSpaAddress = 0; if (!pSpaAddr || !pSpaRangeTable) { return EFI_INVALID_PARAMETER; } StartSpaAddress = pSpaRangeTable->SystemPhysicalAddressRangeBase + pNvDimmRegionTable->RegionOffset; if (pInterleaveTable != NULL) { if (!pInterleaveTable->LineSize || !pInterleaveTable->NumberOfLinesDescribed) { NVDIMM_DBG("Divide by Zero\n"); ReturnCode = EFI_INVALID_PARAMETER; return ReturnCode; } RotationSize = ((UINT64) pInterleaveTable->LineSize) * pInterleaveTable->NumberOfLinesDescribed; RotationNum = Rdpa / RotationSize; LineNum = (UINT32)((Rdpa % RotationSize) / pInterleaveTable->LineSize); *pSpaAddr = StartSpaAddress + RotationNum * RotationSize * pNvDimmRegionTable->InterleaveWays + pInterleaveTable->LinesOffsets[LineNum] * pInterleaveTable->LineSize + Rdpa % pInterleaveTable->LineSize; return ReturnCode; } else { /** TODO: Not Interleaved **/ *pSpaAddr = StartSpaAddress + Rdpa; return ReturnCode; } } /** CopyMemoryAndAddPointerToArray - Copies the data and adds the result pointer to an array of pointers. @param[in, out] ppTable pointer to the array of pointers. Warning! This pointer will be freed. @param[in] pToAdd pointer to the data that the caller wants to add to the array. @param[in] DataSize size of the data that are supposed to be copied. @param[in] NewPointerIndex index in the table that the new pointer should have. @retval NULL - if a memory allocation failed. @retval pointer to the new array of pointers (with the new one at the end). **/ STATIC VOID ** CopyMemoryAndAddPointerToArray( IN OUT VOID **ppTable, IN VOID *pToAdd, IN UINT32 DataSize, IN UINT32 *pNewPointerIndex ) { VOID **ppNewTable = NULL; VOID *pData = NULL; if (pToAdd == NULL) { NVDIMM_ERR("Pointer to data for adding cannot be NULL."); goto Finish; } // Allocate the memory for the new entry to list of tables and for the contents of new entry ppNewTable = AllocatePool(sizeof(VOID *) * (*pNewPointerIndex + 1)); pData = AllocatePool(DataSize); if (ppNewTable == NULL || pData == NULL) { FREE_POOL_SAFE(ppNewTable); FREE_POOL_SAFE(pData); NVDIMM_DBG("Could not allocate the memory."); goto Finish; } // Copy the array beginning only if there is any if (*pNewPointerIndex > 0 && ppTable != NULL) { CopyMem_S(ppNewTable, sizeof(VOID *) * (*pNewPointerIndex + 1), ppTable, sizeof(VOID *) * *pNewPointerIndex); } // Make a copy of the table to add CopyMem_S(pData, DataSize, pToAdd, DataSize); // Assign the new copied table to the array ppNewTable[*pNewPointerIndex] = pData; (*pNewPointerIndex)++; // Increment the array index // Only free caller's original table (ppTable) if we succeed // If we don't succeed, we'll return NULL below FREE_POOL_SAFE(ppTable); Finish: return ppNewTable; } /** Return the current memory mode chosen by the BIOS during boot-up. 1LM is the fallback option and will always be available. 2LM will only be enabled if the AllowedMemoryMode is 2LM, there is memory configured for 2LM, and it is in a BIOS-supported configuration. We read this information from the PCAT table provided by BIOS. @param[out] pResult The current memory mode chosen by BIOS @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_ABORTED PCAT tables not found **/ EFI_STATUS CurrentMemoryMode( OUT MEMORY_MODE *pResult ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttrTable = NULL; NVDIMM_ENTRY(); if (pResult == NULL) { goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL || gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum != 1) { NVDIMM_DBG("Incorrect PCAT tables"); ReturnCode = EFI_ABORTED; goto Finish; } pPlatformConfigAttrTable = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr; if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pPlatformConfigAttrTable)) { PLATFORM_CAPABILITY_INFO *pPlatformCapability = NULL; if (gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo[0] != NULL) { pPlatformCapability = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo[0]; } else { NVDIMM_DBG("There is no PlatformCapability table in PCAT."); ReturnCode = EFI_ABORTED; goto Finish; } *pResult = pPlatformCapability->CurrentMemoryMode.MemoryModeSplit.CurrentVolatileMode; } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPlatformConfigAttrTable)) { PLATFORM_CAPABILITY_INFO3 *pPlatformCapability3 = NULL; if (gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0] != NULL) { pPlatformCapability3 = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0]; } else { NVDIMM_DBG("There is no PlatformCapability table in PCAT."); ReturnCode = EFI_ABORTED; goto Finish; } *pResult = pPlatformCapability3->CurrentMemoryMode.MemoryModeSplit.CurrentVolatileMode; } else { NVDIMM_DBG("Unknown PCAT table revision"); ReturnCode = EFI_ABORTED; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Return the allowed memory mode selected in the BIOS setup menu under Socket Configuration -> Memory Configuration -> Memory Map -> Volatile Memory Mode. Even if 2LM is allowed, it implies that 1LM is allowed as well (even though the memory mode doesn't indicate this). We read this information from the PCAT table provided by BIOS. @param[out] pResult The allowed memory mode setting in BIOS @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_ABORTED PCAT tables not found **/ EFI_STATUS AllowedMemoryMode( OUT MEMORY_MODE *pResult ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttrTable = NULL; NVDIMM_ENTRY(); if (pResult == NULL) { goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL || gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum != 1) { NVDIMM_DBG("Incorrect PCAT tables"); ReturnCode = EFI_ABORTED; goto Finish; } pPlatformConfigAttrTable = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr; if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pPlatformConfigAttrTable)) { PLATFORM_CAPABILITY_INFO *pPlatformCapability = NULL; if (gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo[0] != NULL) { pPlatformCapability = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo[0]; } else { NVDIMM_DBG("There is no PlatformCapability table in PCAT."); ReturnCode = EFI_ABORTED; goto Finish; } *pResult = pPlatformCapability->CurrentMemoryMode.MemoryModeSplit.AllowedVolatileMode; } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPlatformConfigAttrTable)) { PLATFORM_CAPABILITY_INFO3 *pPlatformCapability3 = NULL; if (gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0] != NULL) { pPlatformCapability3 = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0]; } else { NVDIMM_DBG("There is no PlatformCapability table in PCAT."); ReturnCode = EFI_ABORTED; goto Finish; } *pResult = pPlatformCapability3->CurrentMemoryMode.MemoryModeSplit.AllowedVolatileMode; } else { NVDIMM_DBG("Unknown PCAT table revision"); ReturnCode = EFI_ABORTED; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check if BIOS supports changing configuration through management software @param[out] pConfigChangeSupported The Config Change support in BIOS @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_LOAD_ERROR PCAT tables not found **/ EFI_STATUS CheckIfBiosSupportsConfigChange( OUT BOOLEAN *pConfigChangeSupported ) { EFI_STATUS ReturnCode = EFI_LOAD_ERROR; PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttrTable = NULL; NVDIMM_ENTRY(); if (pConfigChangeSupported == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } *pConfigChangeSupported = FALSE; if (gNvmDimmData->PMEMDev.pPcatHead == NULL || gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum != 1) { NVDIMM_DBG("Incorrect PCAT tables"); goto Finish; } pPlatformConfigAttrTable = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr; if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pPlatformConfigAttrTable)) { PLATFORM_CAPABILITY_INFO *pPlatformCapability = NULL; if (gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo[0] != NULL) { pPlatformCapability = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo[0]; } else { NVDIMM_DBG("There is no PlatformCapability table in PCAT."); goto Finish; } if (IS_BIT_SET_VAR(pPlatformCapability->MgmtSwConfigInputSupport, BIT0)) { *pConfigChangeSupported = TRUE; } } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPlatformConfigAttrTable)) { PLATFORM_CAPABILITY_INFO3 *pPlatformCapability3 = NULL; if (gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0] != NULL) { pPlatformCapability3 = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0]; } else { NVDIMM_DBG("There is no PlatformCapability table in PCAT."); goto Finish; } if (IS_BIT_SET_VAR(pPlatformCapability3->MgmtSwConfigInputSupport, BIT0)) { *pConfigChangeSupported = TRUE; } } else { NVDIMM_DBG("Unknown PCAT table revision"); goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check Memory Mode Capabilities from PCAT table type 0 @param[out] pMemoryModeCapabilities pointer to memory mode capabilities @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_LOAD_ERROR PCAT tables not found **/ EFI_STATUS CheckMemModeCapabilities( OUT MEMORY_MODE_CAPABILITIES *pMemoryModeCapabilities ) { EFI_STATUS ReturnCode = EFI_LOAD_ERROR; PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttrTable = NULL; NVDIMM_ENTRY(); if (pMemoryModeCapabilities == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL || gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum != 1) { NVDIMM_DBG("Incorrect PCAT tables"); goto Finish; } pPlatformConfigAttrTable = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr; if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pPlatformConfigAttrTable)) { PLATFORM_CAPABILITY_INFO *pPlatformCapability = NULL; if (gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo[0] != NULL) { pPlatformCapability = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppPlatformCapabilityInfo[0]; } else { NVDIMM_DBG("There is no PlatformCapability table in PCAT."); goto Finish; } CopyMem_S(pMemoryModeCapabilities, sizeof(MEMORY_MODE_CAPABILITIES), &pPlatformCapability->MemoryModeCapabilities, sizeof(MEMORY_MODE_CAPABILITIES)); // Backwards compatibility. Platforms with PCAT revision < 1.x always support mixed mode pMemoryModeCapabilities->MemoryModesFlags.MixedMode = MIXED_MODE_CAPABILITY_SUPPORTED; } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPlatformConfigAttrTable)) { PLATFORM_CAPABILITY_INFO3 *pPlatformCapability3 = NULL; if (gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0] != NULL) { pPlatformCapability3 = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0]; } else { NVDIMM_DBG("There is no PlatformCapability table in PCAT."); goto Finish; } CopyMem_S(pMemoryModeCapabilities, sizeof(MEMORY_MODE_CAPABILITIES), &pPlatformCapability3->MemoryModeCapabilities, sizeof(MEMORY_MODE_CAPABILITIES)); } else { NVDIMM_DBG("Unknown PCAT table revision"); goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve the PCAT Socket SKU Mapped Memory Limit for a given socket @param[in] SocketId SocketID @param[out] pMappedMemoryLimit Pointer to Mapped Memory Limit @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_NOT_FOUND PCAT socket sku mapped memory limit not found for given socketID **/ EFI_STATUS RetrievePcatSocketSkuMappedMemoryLimit( IN UINT32 SocketId, OUT UINT64 *pMappedMemoryLimit ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttrTable = NULL; UINT32 Index = 0; UINT32 LogicalSocketID = 0; NVDIMM_ENTRY(); if (pMappedMemoryLimit == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL || gNvmDimmData->PMEMDev.pPcatHead->SocketSkuInfoNum == 0) { NVDIMM_DBG("Incorrect PCAT tables"); goto Finish; } pPlatformConfigAttrTable = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr; if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pPlatformConfigAttrTable)) { for (Index = 0; Index < gNvmDimmData->PMEMDev.pPcatHead->SocketSkuInfoNum; Index++) { if (SocketId == gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable[Index]->SocketId) { *pMappedMemoryLimit = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable[Index]->MappedMemorySizeLimit; ReturnCode = EFI_SUCCESS; break; } } } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPlatformConfigAttrTable)) { for (Index = 0; Index < gNvmDimmData->PMEMDev.pPcatHead->SocketSkuInfoNum; Index++) { ReturnCode = GetLogicalSocketIdFromPmtt(gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index]->SocketId, gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index]->DieId, &LogicalSocketID); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to retrieve logical socket ID"); goto Finish; } if (SocketId == LogicalSocketID) { *pMappedMemoryLimit = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index]->MappedMemorySizeLimit; ReturnCode = EFI_SUCCESS; break; } } } else { NVDIMM_DBG("Unknown PCAT table revision"); goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve the PCAT Socket SKU Total Mapped Memory for a given socket @param[in] SocketId SocketID, 0xFFFF indicates all sockets @param[out] pTotalMappedMemory Pointer to Total Mapped Memory @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_NOT_FOUND PCAT socket sku total mapped memory not found for given socketID **/ EFI_STATUS RetrievePcatSocketSkuTotalMappedMemory( IN UINT32 SocketId, OUT UINT64 *pTotalMappedMemory ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttrTable = NULL; UINT32 Index = 0; UINT32 LogicalSocketID = 0; NVDIMM_ENTRY(); if (pTotalMappedMemory == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL || gNvmDimmData->PMEMDev.pPcatHead->SocketSkuInfoNum == 0) { NVDIMM_DBG("Incorrect PCAT tables"); goto Finish; } // Setting output parameters to 0 before initialization *pTotalMappedMemory = 0; pPlatformConfigAttrTable = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr; if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pPlatformConfigAttrTable)) { for (Index = 0; Index < gNvmDimmData->PMEMDev.pPcatHead->SocketSkuInfoNum; Index++) { if (SocketId == SOCKET_ID_ALL || SocketId == gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable[Index]->SocketId) { *pTotalMappedMemory += gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable[Index]->TotalMemorySizeMappedToSpa; ReturnCode = EFI_SUCCESS; if (SocketId != SOCKET_ID_ALL) { break; } } } } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPlatformConfigAttrTable)) { for (Index = 0; Index < gNvmDimmData->PMEMDev.pPcatHead->SocketSkuInfoNum; Index++) { ReturnCode = GetLogicalSocketIdFromPmtt(gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index]->SocketId, gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index]->DieId, &LogicalSocketID); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to retrieve logical socket ID"); goto Finish; } if (SocketId == SOCKET_ID_ALL || SocketId == LogicalSocketID) { *pTotalMappedMemory += gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index]->TotalMemorySizeMappedToSpa; ReturnCode = EFI_SUCCESS; if (SocketId != SOCKET_ID_ALL) { break; } } } } else { NVDIMM_DBG("Unknown PCAT table revision"); goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve the PCAT Socket SKU Cached Memory for a given socket @param[in] SocketId SocketID, 0xFFFF indicates all sockets @param[out] pCachedMemory Pointer to Cached Memory Size @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_NOT_FOUND PCAT socket sku cached memory size not found for given socketID **/ EFI_STATUS RetrievePcatSocketSkuCachedMemory( IN UINT32 SocketId, OUT UINT64 *pCachedMemory ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttrTable = NULL; UINT32 Index = 0; UINT32 LogicalSocketID = 0; NVDIMM_ENTRY(); if (pCachedMemory == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL || gNvmDimmData->PMEMDev.pPcatHead->SocketSkuInfoNum == 0) { NVDIMM_DBG("Incorrect PCAT tables"); goto Finish; } // Setting output parameters to 0 before initialization *pCachedMemory = 0; pPlatformConfigAttrTable = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr; if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pPlatformConfigAttrTable)) { for (Index = 0; Index < gNvmDimmData->PMEMDev.pPcatHead->SocketSkuInfoNum; Index++) { if (SocketId == SOCKET_ID_ALL || SocketId == gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable[Index]->SocketId) { *pCachedMemory += gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppSocketSkuInfoTable[Index]->CachingMemorySize; ReturnCode = EFI_SUCCESS; if (SocketId != SOCKET_ID_ALL) { break; } } } } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPlatformConfigAttrTable)) { for (Index = 0; Index < gNvmDimmData->PMEMDev.pPcatHead->SocketSkuInfoNum; Index++) { ReturnCode = GetLogicalSocketIdFromPmtt(gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index]->SocketId, gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index]->DieId, &LogicalSocketID); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Unable to retrieve logical socket ID"); goto Finish; } if (SocketId == SOCKET_ID_ALL || SocketId == LogicalSocketID) { *pCachedMemory += gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppDieSkuInfoTable[Index]->CachingMemorySize; ReturnCode = EFI_SUCCESS; if (SocketId != SOCKET_ID_ALL) { break; } } } } else { NVDIMM_DBG("Unknown PCAT table revision"); goto Finish; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve the list of supported Channel & iMC Interleave sizes @param[out] ppChannelInterleaveSize Array of supported Channel Interleave sizes @param[out] ppiMCInterleaveSize Array of supported iMC Interleave sizes @param[out] ppRecommendedFormats Array of recommended formats @param[out] ppChannelWays Array of supported channel ways @param[out] pLength Length of the array @param[out] pInterleaveAlignmentSize Interleave Alignment Size @param[out] pRevision PCAT Table revision @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES Memory Allocation failure @retval EFI_INVALID_PARAMETER ppChannelInterleaveSize, ppiMCInterleaveSize or pLength is NULL @retval EFI_NOT_FOUND Interleave size info not found **/ EFI_STATUS RetrieveSupportediMcAndChannelInterleaveSizes( OUT UINT32 **ppChannelInterleaveSize, OUT UINT32 **ppiMCInterleaveSize, OUT UINT32 **ppRecommendedFormats, OUT UINT32 **ppChannelWays, OUT UINT32 *pLength, OUT UINT32 *pInterleaveAlignmentSize, OUT ACPI_REVISION *pRevision ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttrTable = NULL; BOOLEAN RetrieveRecommendedFormats = FALSE; BOOLEAN RetrieveChannelWays = FALSE; BOOLEAN RetrieveInterleaveAlignmentSize = FALSE; UINT32 Index = 0; NVDIMM_ENTRY(); if (ppChannelInterleaveSize == NULL || ppiMCInterleaveSize == NULL || pLength == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (ppRecommendedFormats != NULL) { RetrieveRecommendedFormats = TRUE; } if (ppChannelWays != NULL) { RetrieveChannelWays = TRUE; } if (pInterleaveAlignmentSize != NULL) { RetrieveInterleaveAlignmentSize = TRUE; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL || gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum != 1) { NVDIMM_DBG("Incorrect PCAT tables"); goto Finish; } pPlatformConfigAttrTable = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr; if (pRevision != NULL) { *pRevision = pPlatformConfigAttrTable->Header.Revision; } if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID(pPlatformConfigAttrTable)) { MEMORY_INTERLEAVE_CAPABILITY_INFO *pMemoryInterleaveCapability = NULL; if (gNvmDimmData->PMEMDev.pPcatHead->MemoryInterleaveCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo[0] != NULL) { for (Index = 0; Index < gNvmDimmData->PMEMDev.pPcatHead->MemoryInterleaveCapabilityInfoNum; Index++) { if (gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo[Index]->MemoryMode == PCAT_MEMORY_MODE_PM_DIRECT) { pMemoryInterleaveCapability = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo[Index]; break; } } if (pMemoryInterleaveCapability == NULL) { NVDIMM_DBG("There is no Memory Interleave Capability Information table for PM mode."); goto Finish; } } else { NVDIMM_DBG("There is no MemoryInterleaveCapability table in PCAT."); goto Finish; } *ppChannelInterleaveSize = AllocateZeroPool(sizeof(**ppChannelInterleaveSize) * pMemoryInterleaveCapability->NumOfFormatsSupported); if (*ppChannelInterleaveSize == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } *ppiMCInterleaveSize = AllocateZeroPool(sizeof(**ppiMCInterleaveSize) * pMemoryInterleaveCapability->NumOfFormatsSupported); if (*ppiMCInterleaveSize == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } if (RetrieveRecommendedFormats) { *ppRecommendedFormats = AllocateZeroPool(sizeof(**ppRecommendedFormats) * pMemoryInterleaveCapability->NumOfFormatsSupported); if (*ppRecommendedFormats == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } } if (RetrieveChannelWays) { *ppChannelWays = AllocateZeroPool(sizeof(**ppChannelWays) * pMemoryInterleaveCapability->NumOfFormatsSupported); if (*ppChannelWays == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } } for (Index = 0; Index < pMemoryInterleaveCapability->NumOfFormatsSupported; Index++) { (*ppiMCInterleaveSize)[Index] = pMemoryInterleaveCapability->InterleaveFormatList[Index].InterleaveFormatSplit.iMCInterleaveSize; (*ppChannelInterleaveSize)[Index] = pMemoryInterleaveCapability->InterleaveFormatList[Index].InterleaveFormatSplit.ChannelInterleaveSize; if (RetrieveChannelWays) { (*ppChannelWays)[Index] = pMemoryInterleaveCapability->InterleaveFormatList[Index].InterleaveFormatSplit.NumberOfChannelWays; } if (RetrieveRecommendedFormats) { (*ppRecommendedFormats)[Index] = pMemoryInterleaveCapability->InterleaveFormatList[Index].InterleaveFormatSplit.Recommended; } } *pLength = Index; if (RetrieveInterleaveAlignmentSize) { *pInterleaveAlignmentSize = pMemoryInterleaveCapability->InterleaveAlignmentSize; } } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPlatformConfigAttrTable)) { MEMORY_INTERLEAVE_CAPABILITY_INFO3 *pMemoryInterleaveCapability3 = NULL; if (gNvmDimmData->PMEMDev.pPcatHead->MemoryInterleaveCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo[0] != NULL) { for (Index = 0; Index < gNvmDimmData->PMEMDev.pPcatHead->MemoryInterleaveCapabilityInfoNum; Index++) { if (gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat2Tables.ppMemoryInterleaveCapabilityInfo[Index]->MemoryMode == PCAT_MEMORY_MODE_PM_DIRECT) { pMemoryInterleaveCapability3 = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo[Index]; break; } } if (pMemoryInterleaveCapability3 == NULL) { NVDIMM_DBG("There is no Memory Interleave Capability Information table for PM mode."); goto Finish; } } else { NVDIMM_DBG("There is no MemoryInterleaveCapability table in PCAT."); goto Finish; } *ppChannelInterleaveSize = AllocateZeroPool(sizeof(**ppChannelInterleaveSize)); if (*ppChannelInterleaveSize == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } *ppiMCInterleaveSize = AllocateZeroPool(sizeof(**ppiMCInterleaveSize)); if (*ppiMCInterleaveSize == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } (*ppiMCInterleaveSize)[Index] = pMemoryInterleaveCapability3->InterleaveSize.InterleaveSizeSplit.iMCInterleaveSize; (*ppChannelInterleaveSize)[Index] = pMemoryInterleaveCapability3->InterleaveSize.InterleaveSizeSplit.ChannelInterleaveSize; if (RetrieveInterleaveAlignmentSize) { *pInterleaveAlignmentSize = pMemoryInterleaveCapability3->InterleaveAlignmentSize; } if (RetrieveChannelWays) { *ppChannelWays = NULL; } if (RetrieveRecommendedFormats) { *ppRecommendedFormats = NULL; } *pLength = 1; } else { NVDIMM_DBG("Unknown PCAT table revision"); ReturnCode = EFI_ABORTED; goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve InterleaveSetMap Info @param[out] ppInterleaveMap Info List used to determine the best interleave based on requested DCPMMs @param[out] pInterleaveMapListLength Pointer to the InterleaveSetMap Length @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES Memory Allocation failure @retval EFI_INVALID_PARAMETER ppInterleaveSetMap or InterleaveMapListLength is NULL @retval EFI_NOT_FOUND InterleaveSetMap Info not found **/ EFI_STATUS RetrieveInterleaveSetMap( OUT UINT32 **ppInterleaveMap, OUT UINT32 *pInterleaveMapListLength ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttrTable = NULL; MEMORY_INTERLEAVE_CAPABILITY_INFO3 *pMemoryInterleaveCapability = NULL; UINT32 Index = 0; NVDIMM_ENTRY(); if (ppInterleaveMap == NULL || pInterleaveMapListLength == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL || gNvmDimmData->PMEMDev.pPcatHead->MemoryInterleaveCapabilityInfoNum != 1) { NVDIMM_DBG("Incorrect PCAT tables"); goto Finish; } pPlatformConfigAttrTable = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr; if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPlatformConfigAttrTable)) { if (gNvmDimmData->PMEMDev.pPcatHead->MemoryInterleaveCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo[0] != NULL) { pMemoryInterleaveCapability = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo[0]; } else { NVDIMM_DBG("There is no MemoryInterleaveCapability table in PCAT."); goto Finish; } *ppInterleaveMap = AllocateZeroPool(sizeof(**ppInterleaveMap) * pMemoryInterleaveCapability->NumOfFormatsSupported); if (*ppInterleaveMap == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } *pInterleaveMapListLength = 0; for (Index = 0; Index < pMemoryInterleaveCapability->NumOfFormatsSupported; Index++) { (*ppInterleaveMap)[Index] = pMemoryInterleaveCapability->InterleaveFormatList[Index].InterleaveFormatSplit.InterleaveMap; } *pInterleaveMapListLength = Index; } else { NVDIMM_DBG("Unknown PCAT table revision"); goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve Channel ways from InterleaveSetMap Info @param[out] ppChannelWays Array of channel ways supported @param[out] pChannelWaysListLength Pointer to the ppChannelWays array Length @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES Memory Allocation failure @retval EFI_INVALID_PARAMETER ppInterleaveSetMap or InterleaveMapListLength is NULL @retval EFI_NOT_FOUND InterleaveSetMap Info not found **/ EFI_STATUS RetrieveChannelWaysFromInterleaveSetMap( OUT UINT32 **ppChannelWays, OUT UINT32 *pChannelWaysListLength ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttrTable = NULL; MEMORY_INTERLEAVE_CAPABILITY_INFO3 *pMemoryInterleaveCapability = NULL; UINT32 Index = 0; UINT8 NumOfBitsSet = 0; UINT8 PrevNumOfBitsSet = 0; UINT32 Length = 0; NVDIMM_ENTRY(); if (ppChannelWays == NULL || pChannelWaysListLength == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL || gNvmDimmData->PMEMDev.pPcatHead->MemoryInterleaveCapabilityInfoNum != 1) { NVDIMM_DBG("Incorrect PCAT tables"); goto Finish; } pPlatformConfigAttrTable = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr; if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPlatformConfigAttrTable)) { if (gNvmDimmData->PMEMDev.pPcatHead->MemoryInterleaveCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo[0] != NULL) { pMemoryInterleaveCapability = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppMemoryInterleaveCapabilityInfo[0]; } else { NVDIMM_DBG("There is no MemoryInterleaveCapability table in PCAT."); goto Finish; } *pChannelWaysListLength = 0; for (Index = 0; Index < pMemoryInterleaveCapability->NumOfFormatsSupported; Index++) { ReturnCode = CountNumOfBitsSet(pMemoryInterleaveCapability->InterleaveFormatList[Index].InterleaveFormatSplit.InterleaveMap, &NumOfBitsSet); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("CountNumOfBitsSet failed"); goto Finish; } if (PrevNumOfBitsSet == NumOfBitsSet) { continue; } *ppChannelWays = ReallocatePool(sizeof(UINT32) * Length, sizeof(UINT32) * (Length + 1), *ppChannelWays); if (*ppChannelWays == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } switch (NumOfBitsSet) { case 1: (*ppChannelWays)[Length] = INTERLEAVE_SET_1_WAY; break; case 2: (*ppChannelWays)[Length] = INTERLEAVE_SET_2_WAY; break; case 3: (*ppChannelWays)[Length] = INTERLEAVE_SET_3_WAY; break; case 4: (*ppChannelWays)[Length] = INTERLEAVE_SET_4_WAY; break; case 6: (*ppChannelWays)[Length] = INTERLEAVE_SET_6_WAY; break; case 8: (*ppChannelWays)[Length] = INTERLEAVE_SET_8_WAY; break; case 12: (*ppChannelWays)[Length] = INTERLEAVE_SET_12_WAY; break; case 16: (*ppChannelWays)[Length] = INTERLEAVE_SET_16_WAY; break; case 24: (*ppChannelWays)[Length] = INTERLEAVE_SET_24_WAY; break; default: NVDIMM_WARN("Unsupported number of DIMMs in interleave set: %d", NumOfBitsSet); (*ppChannelWays)[Length] = 0; break; } PrevNumOfBitsSet = NumOfBitsSet; Length++; } // BIOS does not include x1 (non-interleaved) // since it is always supported *ppChannelWays = ReallocatePool(sizeof(UINT32) * Length, sizeof(UINT32) * (Length + 1), *ppChannelWays); if (*ppChannelWays == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } (*ppChannelWays)[Length] = INTERLEAVE_SET_1_WAY; Length++; *pChannelWaysListLength = Length; } else { NVDIMM_DBG("Unknown PCAT table revision"); goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve Maximum PM Interleave Sets per Die & DCPMM @param[out] pMaxPMInterleaveSets pointer to Maximum PM Interleave Sets per Die & Dcpmm @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pMaxPMInterleaveSetsPerDie is NULL @retval EFI_NOT_FOUND MaxPMInterleaveSets not found **/ EFI_STATUS RetrieveMaxPMInterleaveSets( OUT MAX_PMINTERLEAVE_SETS *pMaxPMInterleaveSets ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttrTable = NULL; PLATFORM_CAPABILITY_INFO3 *pPlatformCapabilityInfo = NULL; NVDIMM_ENTRY(); if (pMaxPMInterleaveSets == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL || gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum != 1) { NVDIMM_DBG("Incorrect PCAT tables"); goto Finish; } pPlatformConfigAttrTable = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr; if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3(pPlatformConfigAttrTable)) { if (gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0] != NULL) { pPlatformCapabilityInfo = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0]; } else { NVDIMM_DBG("There is no PlatformCapabilityInfo table in PCAT."); goto Finish; } CopyMem(pMaxPMInterleaveSets, &pPlatformCapabilityInfo->MaxPMInterleaveSets, sizeof(*pMaxPMInterleaveSets)); } else { NVDIMM_DBG("Unknown PCAT table revision"); goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve PCAT DDR Cache Size per channel in bytes from PCAT PlatformCapabilityInfo table @param[out] pDDRCacheSize pointer to DDR Cache Size @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDDRCacheSize is NULL @retval EFI_NOT_FOUND DDRCacheSize not found **/ EFI_STATUS RetrievePcatDDRCacheSize( OUT UINT64 *pDDRCacheSize ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttrTable = NULL; PLATFORM_CAPABILITY_INFO3 *pPlatformCapabilityInfo = NULL; NVDIMM_ENTRY(); if (pDDRCacheSize == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL || gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 0) { NVDIMM_DBG("Incorrect PCAT tables"); goto Finish; } pPlatformConfigAttrTable = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr; if (IS_ACPI_HEADER_REV_MAJ_3_MIN_VALID(pPlatformConfigAttrTable)) { if (gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0] != NULL) { pPlatformCapabilityInfo = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0]; } else { NVDIMM_DBG("There is no PlatformCapabilityInfo table in PCAT."); goto Finish; } *pDDRCacheSize = GIB_TO_BYTES(pPlatformCapabilityInfo->DDRCacheSize); } else { NVDIMM_DBG("Unknown PCAT table revision"); goto Finish; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Check PCAT Cache Capabilities to see if cross-tile caching is supported @param[out] pCrossTileCachingSupported pointer to cross-tile supported boolean flag @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pCrossTileCachingSupported is NULL @retval EFI_NOT_FOUND CrossTileCachingSupport not found **/ EFI_STATUS CheckIsCrossTileCachingSupported( OUT BOOLEAN *pCrossTileCachingSupported ) { EFI_STATUS ReturnCode = EFI_NOT_FOUND; PLATFORM_CONFIG_ATTRIBUTES_TABLE *pPlatformConfigAttrTable = NULL; PLATFORM_CAPABILITY_INFO3 *pPlatformCapabilityInfo = NULL; NVDIMM_ENTRY(); if (pCrossTileCachingSupported == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL || gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 0) { NVDIMM_DBG("Incorrect PCAT tables"); goto Finish; } pPlatformConfigAttrTable = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr; if (IS_ACPI_HEADER_REV_MAJ_3_MIN_VALID(pPlatformConfigAttrTable)) { if (gNvmDimmData->PMEMDev.pPcatHead->PlatformCapabilityInfoNum == 1 && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo != NULL && gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0] != NULL) { pPlatformCapabilityInfo = gNvmDimmData->PMEMDev.pPcatHead->pPcatVersion.Pcat3Tables.ppPlatformCapabilityInfo[0]; } else { NVDIMM_DBG("There is no PlatformCapabilityInfo table in PCAT."); goto Finish; } *pCrossTileCachingSupported = pPlatformCapabilityInfo->CacheCapabilities.CacheCapabilitiesSplit.MemoryMode.CrossTile; } else { NVDIMM_DBG("Unknown PCAT table revision"); *pCrossTileCachingSupported = FALSE; } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; }ipmctl-03.00.00.0485/DcpmPkg/driver/Utils/AcpiParsing.h000066400000000000000000000501131440615110200221040ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _ACPI_PARSING_H_ #define _ACPI_PARSING_H_ #include #include #include #include extern GUID gSpaRangeVolatileRegionGuid; extern GUID gSpaRangePmRegionGuid; extern GUID gSpaRangeControlRegionGuid; extern GUID gSpaRangeBlockDataWindowRegionGuid; extern GUID gSpaRangeRawVolatileRegionGuid; extern GUID gSpaRangeIsoVolatileRegionGuid; extern GUID gSpaRangeRawPmRegionGuid; extern GUID gSpaRangeIsoPmRegionGuid; extern GUID gAppDirectPmTypeGuid; extern GUID gSpaRangeMailboxCustomGuid; #define SPA_RANGE_VOLATILE_REGION_GUID \ { 0x7305944F, 0xFDDA, 0x44E3, {0xB1, 0x6C, 0x3F, 0x22, 0xD2, 0x52, 0xE5, 0xD0} } #define SPA_RANGE_PM_REGION_GUID \ { 0x66F0D379, 0xB4F3, 0x4074, {0xAC, 0x43, 0x0D, 0x33, 0x18, 0xB7, 0x8C, 0xDB} } #define SPA_RANGE_CONTROL_REGION_GUID \ { 0x92F701F6, 0x13B4, 0x405D, {0x91, 0x0B, 0x29, 0x93, 0x67, 0xE8, 0x23, 0x4C} } #define SPA_RANGE_BLOCK_DATA_WINDOW_REGION_GUID \ { 0x91AF0530, 0x5D86, 0x470E, {0xA6, 0xB0, 0x0A, 0x2D, 0xB9, 0x40, 0x82, 0x49} } #define SPA_RANGE_RAW_VOLATILE \ { 0x77AB535A, 0x45FC, 0x624B, {0x55, 0x60, 0xF7, 0xB2, 0x81, 0xD1, 0xF9, 0x6E} } #define SPA_RANGE_ISO_VOLATILE \ { 0x3D5ABD30, 0x4175, 0x87CE, {0x6D, 0x64, 0xD2, 0xAD, 0xE5, 0x23, 0xC4, 0xBB} } #define SPA_RANGE_RAW_PM \ { 0x5CEA02C9, 0x4D07, 0x69D3, {0x26, 0x9F, 0x44, 0x96, 0xFB, 0xE0, 0x96, 0xF9} } #define SPA_RANGE_ISO_PM \ { 0x08018188, 0x42CD, 0xBB48, {0x10, 0x0F, 0x53, 0x87, 0xD5, 0x3D, 0xED, 0x3D} } #define APPDIRECT_PM_TYPE \ { 0x66F0D379, 0xB4F3, 0x4074, {0xAC, 0x43, 0x0D, 0x33, 0x18, 0xB7, 0x8C, 0xDB} } /* * SPA_RANGE_CONTROL_REGION_GUID above should only be used for block windows. * This is a custom GUID so that we can find the mailbox spa range structs. */ #define SPA_RANGE_MAILBOX_CUSTOM_GUID \ { 0x48D7624D, 0x5CD8, 0x4924, {0xAF, 0xD5, 0xDB, 0xCB, 0xF8, 0x50, 0xCC, 0x2B} } #define NFIT_ACPI_NAMESPACE_ID SIGNATURE_64('A', 'C', 'P', 'I', '0', '0', '1', '0') #define NFIT_TABLE_SIG SIGNATURE_32('N', 'F', 'I', 'T') //!< NFIT Table signature #define PCAT_TABLE_SIG SIGNATURE_32('P', 'C', 'A', 'T') //!< PCAT Table signature #define PMTT_TABLE_SIG SIGNATURE_32('P', 'M', 'T', 'T') //!< PMTT Table signature /** Offset of the MinFixed field in the Nfit Acpi Namespace. This field contains the pointer to the NFIT table. **/ #define NFIT_POINTER_OFFSET 0x2A typedef enum { MEMORY_MODE_1LM = 0, MEMORY_MODE_2LM = 1, MEMORY_MODE_1LM_PLUS_2LM = 2 } MEMORY_MODE; typedef SUPPORTED_MEMORY_MODE3 MEMORY_MODE_CAPABILITIES; #define IS_BIOS_VOLATILE_MEMORY_MODE_2LM(VolatileMemoryMode) ((MEMORY_MODE_2LM == VolatileMemoryMode) || (MEMORY_MODE_1LM_PLUS_2LM == VolatileMemoryMode)) /** ACPI Related Functions **/ /** ParseNfitTable - Performs deserialization from binary memory block into parsed structure of pointers. @param[in] pTable pointer to the memory containing the NFIT binary representation. @param[out] ppParsedNfit Pointer to a pointer where the allocated and parsed NFIT table will be stored @retval EFI_INVALID_PARAMETER One of the provided parameters is invalid @retval EFI_VOLUME_CORRUPTED If the table checksum is invalid @retval EFI_INCOMPATIBLE_VERSION If the table is not compatible with this ipmctl version @retval EFI_SUCCESS **/ EFI_STATUS ParseNfitTable( IN VOID *pTable, OUT ParsedFitHeader **ppParsedNfit ); /** Performs deserialization from binary memory block, containing PCAT tables, into parsed structure of pointers. @param[in] pTable pointer to the memory containing the PCAT binary representation. @param[out] ppParsedPcat Pointer to a pointer where the allocated and parsed PCAT table will be stored @retval EFI_INVALID_PARAMETER One of the provided parameters is invalid @retval EFI_VOLUME_CORRUPTED If the table checksum is invalid @retval EFI_INCOMPATIBLE_VERSION If the table is not compatible with this ipmctl version @retval EFI_SUCCESS **/ EFI_STATUS ParsePcatTable ( IN VOID *pTable, OUT ParsedPcatHeader **ppParsedPcat ); /** Performs deserialization from binary memory block, containing PMTT tables, into parsed structure of pointers. @param[in] pTable pointer to the memory containing the PMTT binary representation. @param[out] ppParsedPmtt Pointer to a pointer where the allocated and parsed PMTT table will be stored @retval EFI_INVALID_PARAMETER One of the provided parameters is invalid @retval EFI_VOLUME_CORRUPTED If the table checksum is invalid @retval EFI_INCOMPATIBLE_VERSION If the table is not compatible with this ipmctl version @retval EFI_SUCCESS **/ EFI_STATUS ParsePmttTable( IN VOID *pTable, OUT ParsedPmttHeader **ppParsedPmtt ); /** Get PMTT Dimm Module by Dimm ID Scan the dimm list for a dimm identified by Dimm ID @param[in] DimmID: The SMBIOS Type 17 handle of the dimm @param[in] pPmttHead: Parsed PMTT Table @retval PMTT_MODULE_INFO struct pointer if matching dimm has been found @retval NULL pointer if not found **/ PMTT_MODULE_INFO * GetDimmModuleByPidFromPmtt( IN UINT32 DimmID, IN ParsedPmttHeader *pPmttHead ); /** Retrieve the Logical Socket ID from PMTT Table @param[in] SocketId SocketID @param[in] DieId DieID @param[out] pLogicalSocketId Logical socket ID based on Dimm socket ID & Die ID @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_NOT_FOUND PCAT socket sku info table not found for given socketID **/ EFI_STATUS GetLogicalSocketIdFromPmtt( IN UINT32 SocketId, IN UINT32 DieId, OUT UINT32 *pLogicalSocketId ); /** Check if the current population is a special non-por config supported when cross-tiling is enabled @param[out] pNonPorCrossTileSupportedConfig pointer to non-por config supported boolean flag @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pNonPorCrossTileSupportedConfig is NULL @retval EFI_NOT_FOUND Parsed PMTT table is NULL **/ EFI_STATUS CheckIsNonPorCrossTileSupportedConfig( OUT BOOLEAN *pNonPorCrossTileSupportedConfig ); /** Retrieve the platform topology information (iMCs per die, Channels per iMc) @param[out] piMCsNumPerDie Pointer to number of iMCs per die @param[out] pChannelsNumPeriMC Pointer to number of channels per iMC @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_NOT_FOUND Parsed PMTT table pointer is NULL **/ EFI_STATUS RetrievePlatformTopologyFromPmtt( OUT UINT32 *piMCsNumPerDie, OUT UINT32 *pChannelsNumPeriMC ); /** Conversion Functions **/ /** Returns the FlushHint table associated with the provided NVDIMM region table. @param[in] pFitHead pointer to the parsed NFit Header structure. @param[in] pNvDimmRegionMappingStructure the NVDIMM region table that contains the index. @param[out] ppFlushHintTable pointer to a pointer where the table will be stored. @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more parameters equal NULL. @retval EFI_NOT_FOUND if there is no Interleave table with the provided index. **/ EFI_STATUS GetFlushHintTableForNvDimmRegionTable( IN ParsedFitHeader *pFitHead, IN NvDimmRegionMappingStructure *pNvDimmRegionMappingStructure, OUT FlushHintTbl **ppFlushHintTable ); /** GetBlockDataWindowRegDescTabl - returns the Block Data Window Table associated with the provided Control Region Table. @param[in] pFitHead pointer to the parsed NFit Header structure. @param[in] pControlRegionTable the Control Region table that contains the index. @param[out] ppBlockDataWindowTable pointer to a pointer where the table will be stored. @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if pFitHead or ControlRegionTbl or BWRegionTbl equals NULL. @retval EFI_NOT_FOUND if there is no Block Data Window Descriptor table with the provided index. **/ EFI_STATUS GetBlockDataWindowRegDescTabl( IN ParsedFitHeader *pFitHead, IN ControlRegionTbl *pControlRegTbl, OUT BWRegionTbl **ppBlockDataWindowTable ); /** Returns the ControlRegion table associated with the provided NVDIMM region table. @param[in] pFitHead pointer to the parsed NFit Header structure. @param[in] pNvDimmRegionMappingStructure the NVDIMM region table that contains the index. @param[out] ppControlRegionTable pointer to a pointer where the table will be stored. @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if one or more input parameters equal NULL. @retval EFI_NOT_FOUND if there is no Control Region table with the provided index. **/ EFI_STATUS GetControlRegionTableForNvDimmRegionTable( IN ParsedFitHeader *pFitHead, IN NvDimmRegionMappingStructure *pNvDimmRegionMappingStructure, OUT ControlRegionTbl **ppControlRegionTable ); /** Get Control Region table for provided PhysicalID @param[in] pFitHead pointer to the parsed NFit Header structure @param[in] Pid Dimm PhysicalID @param[out] pControlRegionTables array to store Control Region tables pointers @param[in, out] pControlRegionTablesNum size of array on input, number of items stored in the array on output @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER One or more parameters are NULL @retval EFI_BUFFER_TOO_SMALL There is more Control Region tables in NFIT than size of provided array **/ EFI_STATUS GetControlRegionTablesForPID( IN ParsedFitHeader *pFitHead, IN UINT16 Pid, OUT ControlRegionTbl *pControlRegionTables[], IN OUT UINT32 *pControlRegionTablesNum ); /** GetSpaRangeTable - returns the SpaRange Table with the provided Index. @param[in] pFitHead pointer to the parsed NFit Header structure. @param[in] SpaRangeTblIndex index of the table to be found. @param[out] ppSpaRangeTbl pointer to a pointer where the table will be stored. @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if pFitHead or ppInterleaveTbl equals NULL. @retval EFI_NOT_FOUND if there is no Interleave table with the provided index. **/ EFI_STATUS GetSpaRangeTable( IN ParsedFitHeader *pFitHead, IN UINT16 SpaRangeTblIndex, OUT SpaRangeTbl **ppSpaRangeTbl ); /** Finds in the provided Nfit structure the requested NVDIMM region. If the pAddrRangeTypeGuid equals NULL, the first table matching the Pid will be returned. @param[in] pFitHead pointer to the parsed NFit Header structure. @param[in] Pid the Dimm ID that the NVDIMM region must be for. @param[in] pAddrRangeTypeGuid pointer to GUID type of the range that we are looking for. OPTIONAL @param[in] SpaRangeIndexProvided Determine if SpaRangeIndex is provided @param[in] SpaRangeIndex Looking for NVDIMM region table that is related with provided SPA table. OPTIONAL @param[out] ppNvDimmRegionMappingStructure pointer to a pointer for the return NVDIMM region. @retval EFI_SUCCESS if the table was found and was returned. @retval EFI_INVALID_PARAMETER if one or more input parameters equal NULL. @retval EFI_NOT_FOUND if there is no NVDIMM region for the provided Dimm PID and AddrRangeType. **/ EFI_STATUS GetNvDimmRegionMappingStructureForPid( IN ParsedFitHeader *pFitHead, IN UINT16 Pid, IN GUID *pAddrRangeTypeGuid OPTIONAL, IN BOOLEAN SpaRangeIndexProvided, IN UINT16 SpaRangeIndex OPTIONAL, OUT NvDimmRegionMappingStructure **ppNvDimmRegionMappingStructure ); /** GetInterleaveTable - returns the Interleave Table with the provided Index. @param[in] pFitHead pointer to the parsed NFit Header structure. @param[in] InterleaveTblIndex index of the table to be found. @param[out] ppInterleaveTbl pointer to a pointer where the table will be stored. @retval EFI_SUCCESS if the table was found and is properly returned. @retval EFI_INVALID_PARAMETER if pFitHead or ppInterleaveTbl equals NULL. @retval EFI_NOT_FOUND if there is no Interleave table with the provided index. **/ EFI_STATUS GetInterleaveTable( IN ParsedFitHeader *pFitHead, IN UINT16 InterleaveTblIndex, OUT InterleaveStruct **ppInterleaveTbl ); /** RdpaToSpa() - Convert Device Region Physical to System Physical Address @param[in] Rdpa Device Region Physical Address to convert @param[in] pNvDimmRegionTable The NVDIMM region that helps describe this region of memory @param[in] pInterleaveTable Interleave table referenced by the MemDevToSpaRangeTable @param[out] SpaAddr output for SPA address A memory device could have multiple regions. As such we cannot convert to a device physical address. Instead we refer to the address for a region within the device as device region physical address (RDPA), where Rdpa is a zero based address from the start of the region within the device. @retval EFI_SUCCESS on success @retval EFI_INVALID_PARAMETER on a divide by zero error **/ EFI_STATUS RdpaToSpa( IN UINT64 Rdpa, IN NvDimmRegionMappingStructure *pNvDimmRegionTable, IN SpaRangeTbl *pSpaRangeTable, IN InterleaveStruct *pInterleaveTable OPTIONAL, OUT UINT64 *pSpaAddr ); /** Return the current memory mode chosen by the BIOS during boot-up. 1LM is the fallback option and will always be available. 2LM will only be enabled if the AllowedMemoryMode is 2LM, there is memory configured for 2LM, and it is in a BIOS-supported configuration. We read this information from the PCAT table provided by BIOS. @param[out] pResult The current memory mode chosen by BIOS @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_ABORTED PCAT tables not found **/ EFI_STATUS CurrentMemoryMode( OUT MEMORY_MODE *pResult ); /** Return the allowed memory mode selected in the BIOS setup menu under Socket Configuration -> Memory Configuration -> Memory Map -> Volatile Memory Mode. Even if 2LM is allowed, it implies that 1LM is allowed as well (even though the memory mode doesn't indicate this). We read this information from the PCAT table provided by BIOS. @param[out] pResult The allowed memory mode setting in BIOS @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_ABORTED PCAT tables not found **/ EFI_STATUS AllowedMemoryMode( OUT MEMORY_MODE *pResult ); /** Check if BIOS supports changing configuration through management software @param[out] pConfigChangeSupported The Config Change support in BIOS @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_LOAD_ERROR PCAT tables not found **/ EFI_STATUS CheckIfBiosSupportsConfigChange( OUT BOOLEAN *pConfigChangeSupported ); /** Check Memory Mode Capabilities from PCAT table type 0 @param[out] pMemoryModeCapabilities pointer to memory mode capabilities @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_LOAD_ERROR PCAT tables not found **/ EFI_STATUS CheckMemModeCapabilities( OUT MEMORY_MODE_CAPABILITIES *pMemoryModeCapabilities ); /** Retrieve the PCAT Socket SKU Mapped Memory Limit for a given socket @param[in] SocketId SocketID to retrieve the table for @param[out] pMappedMemoryLimit Pointer to Mapped Memory Limit @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_NOT_FOUND PCAT socket sku mapped memory limit not found for given socketID **/ EFI_STATUS RetrievePcatSocketSkuMappedMemoryLimit( IN UINT32 SocketId, OUT UINT64 *pMappedMemoryLimit ); /** Retrieve the PCAT Socket SKU Total Mapped Memory for a given socket @param[in] SocketId SocketID, 0xFFFF indicates all sockets @param[out] pTotalMappedMemory Pointer to Total Mapped Memory @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_NOT_FOUND PCAT socket sku total mapped memory not found for given socketID **/ EFI_STATUS RetrievePcatSocketSkuTotalMappedMemory( IN UINT32 SocketId, OUT UINT64 *pTotalMappedMemory ); /** Retrieve the PCAT Socket SKU Cached Memory for a given socket @param[in] SocketId SocketID, 0xFFFF indicates all sockets @param[out] pCachedMemory Pointer to Cached Memory Size @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER Input parameter is NULL @retval EFI_NOT_FOUND PCAT socket sku cached memory size not found for given socketID **/ EFI_STATUS RetrievePcatSocketSkuCachedMemory( IN UINT32 SocketId, OUT UINT64 *pCachedMemory ); /** Retrieve the list of supported Channel & iMC Interleave sizes @param[out] ppChannelInterleaveSize Array of supported Channel Interleave sizes @param[out] ppiMCInterleaveSize Array of supported iMC Interleave sizes @param[out] ppRecommendedFormats Array of recommended formats @param[out] ppChannelWays Array of supported channel ways @param[out] pLength Length of the array @param[out] pInterleaveAlignmentSize Interleave Alignment Size @param[out] pRevision PCAT Table revision @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES Memory Allocation failure @retval EFI_INVALID_PARAMETER ppChannelInterleaveSize, ppiMCInterleaveSize or pLength is NULL @retval EFI_NOT_FOUND Interleave size info not found **/ EFI_STATUS RetrieveSupportediMcAndChannelInterleaveSizes( OUT UINT32 **ppChannelInterleaveSize, OUT UINT32 **ppiMCInterleaveSize, OUT UINT32 **ppRecommendedFormats, OUT UINT32 **ppChannelWays, OUT UINT32 *pLength, OUT UINT32 *pInterleaveAlignmentSize, OUT ACPI_REVISION *pRevision ); /** Retrieve InterleaveSetMap Info @param[out] ppInterleaveMap Info List used to determine the best interleave based on requested DCPMMs @param[out] pInterleaveMapListLength Pointer to the InterleaveSetMap Length @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES Memory Allocation failure @retval EFI_INVALID_PARAMETER ppInterleaveSetMap or InterleaveMapListLength is NULL @retval EFI_NOT_FOUND InterleaveSetMap Info not found **/ EFI_STATUS RetrieveInterleaveSetMap( OUT UINT32 **ppInterleaveMap, OUT UINT32 *pInterleaveMapListLength ); /** Retrieve Channel ways from InterleaveSetMap Info @param[out] ppChannelWays Array of channel ways supported @param[out] pChannelWaysListLength Pointer to the ppChannelWays array Length @retval EFI_SUCCESS Success @retval EFI_OUT_OF_RESOURCES Memory Allocation failure @retval EFI_INVALID_PARAMETER ppInterleaveSetMap or InterleaveMapListLength is NULL @retval EFI_NOT_FOUND InterleaveSetMap Info not found **/ EFI_STATUS RetrieveChannelWaysFromInterleaveSetMap( OUT UINT32 **ppChannelWays, OUT UINT32 *pChannelWaysListLength ); /** Performs deserialization from binary memory block containing PMTT table and checks if memory mode can be configured. @param[in] pTable pointer to the memory containing the PMTT binary representation. @retval false if topology does NOT allows MM. @retval true if topology allows MM. **/ BOOLEAN CheckIsMemoryModeAllowed( IN TABLE_HEADER *pPMTT ); /** Retrieve Maximum PM Interleave Sets per Die & DCPMM @param[out] pMaxPMInterleaveSets Pointer to Maximum PM Interleave Sets per Die & Dcpmm @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pMaxPMInterleaveSetsPerDie or pMaxPMInterleaveSetsPerDcpmm is NULL @retval EFI_NOT_FOUND InterleaveSetMap Info not found **/ EFI_STATUS RetrieveMaxPMInterleaveSets( OUT MAX_PMINTERLEAVE_SETS *pMaxPMInterleaveSets ); /** Retrieve PCAT DDR Cache Size per channel in bytes from PCAT PlatformCapabilityInfo table @param[out] pDDRCacheSize pointer to DDR Cache Size @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pDDRCacheSize is NULL @retval EFI_NOT_FOUND DDRCacheSize not found **/ EFI_STATUS RetrievePcatDDRCacheSize( OUT UINT64 *pDDRCacheSize ); /** Check PCAT Cache Capabilities to see if cross-tile caching is supported @param[out] pCrossTileCachingSupported pointer to cross-tile supported boolean flag @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pCrossTileCachingSupported is NULL @retval EFI_NOT_FOUND CrossTileCachingSupport not found **/ EFI_STATUS CheckIsCrossTileCachingSupported( OUT BOOLEAN *pCrossTileCachingSupported ); #endif ipmctl-03.00.00.0485/DcpmPkg/driver/Utils/DumpLoadRegions.c000066400000000000000000000560451440615110200227450ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "DumpLoadRegions.h" #include #include #include #include "Utility.h" #include "Namespace.h" #include extern NVMDIMMDRIVER_DATA *gNvmDimmData; /** Write dump Pool Goal Configuration header to file @param[in] FileHandle File handle to write to @retval EFI_SUCCESS Header written. @retval other Error codes from Write function. **/ EFI_STATUS WriteDumpFileHeader( IN EFI_FILE_HANDLE FileHandle ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; CHAR8 *pHeaderBuffer = NULL; pHeaderBuffer = AllocateZeroPool(MAX_LINE_BYTE_LENGTH); if (pHeaderBuffer == NULL) { NVDIMM_DBG("Could not allocate memory for Ascii buffer."); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } #ifdef _MSC_VER // file is open in binary mode so have to include carriage return in Windows version AsciiSPrint(pHeaderBuffer, MAX_LINE_BYTE_LENGTH, "#SocketID,DimmHandle,Capacity,MemorySize," "AppDirect1Size,AppDirect1Format,AppDirect1Index," "AppDirect2Size,AppDirect2Format,AppDirect2Index\r\n"); #else AsciiSPrint(pHeaderBuffer, MAX_LINE_BYTE_LENGTH, "#SocketID,DimmHandle,Capacity,MemorySize," "AppDirect1Size,AppDirect1Format,AppDirect1Index," "AppDirect2Size,AppDirect2Format,AppDirect2Index\n"); #endif ReturnCode = WriteAsciiLine(FileHandle, pHeaderBuffer); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed in dump dimm config description."); goto Finish; } Finish: FREE_POOL_SAFE(pHeaderBuffer); return ReturnCode; } /** Dump Pool Goal Configurations into file @param[in] pFileHandle File handler @param[in] DimmConfigs Array of DIMM_CONFIG @param[in] DimmConfigsNum Size of DimmConfigs array @retval EFI_SUCCESS Pool Goal Configuration dump successful @retval EFI_INVALID_PARAMETER Invalid Parameter during dump @retval EFI_OUT_OF_RESOURCES Not enough memory to dump @retval EFI_NO_MAPPING Wrong Pool Goal Configuration status **/ EFI_STATUS DumpConfigToFile( IN EFI_FILE_HANDLE pFileHandle, IN DIMM_CONFIG DimmConfigs[], IN UINT32 DimmConfigsNum ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; CHAR8 *pLineBuffer = NULL; DIMM_CONFIG *pDimmConfig = NULL; if (pFileHandle == NULL || DimmConfigs == NULL) { NVDIMM_DBG("Invalid pointer in Dump function."); goto Finish; } pLineBuffer = AllocateZeroPool(MAX_LINE_BYTE_LENGTH); if (pLineBuffer == NULL) { NVDIMM_DBG("Could not allocate memory for unicode line buffer."); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } /** Dump each dimm current config as a row in file **/ for (Index = 0; Index < DimmConfigsNum; Index++) { pDimmConfig = &(DimmConfigs[Index]); ZeroMem(pLineBuffer, MAX_LINE_BYTE_LENGTH); /** Rounding down the capacities to match show goal implementation. **/ pDimmConfig->Capacity = ROUNDDOWN(pDimmConfig->Capacity, BYTES_IN_GIBIBYTE); pDimmConfig->VolatileSize = ROUNDDOWN(pDimmConfig->VolatileSize, BYTES_IN_GIBIBYTE); /** Make sure that persistent sizes are divisible by GiB **/ if (pDimmConfig->Capacity % BYTES_IN_GIBIBYTE != 0 || pDimmConfig->VolatileSize % BYTES_IN_GIBIBYTE != 0 || pDimmConfig->Persistent[FIRST_POOL_GOAL].PersistentSize % BYTES_IN_GIBIBYTE != 0 || pDimmConfig->Persistent[SECOND_POOL_GOAL].PersistentSize % BYTES_IN_GIBIBYTE != 0) { NVDIMM_DBG("Config sizes are not aligned to GiB."); ReturnCode = EFI_ABORTED; goto Finish; } /** Prepare a line to save to file **/ #ifdef _MSC_VER // file is open in binary mode so have to include carriage return in Windows version AsciiSPrint(pLineBuffer, MAX_LINE_BYTE_LENGTH, "%d,%d,%lld,%lld,%lld,%lu,%d,%lld,%lu,%d\r\n", #else AsciiSPrint(pLineBuffer, MAX_LINE_BYTE_LENGTH, "%d,%d,%lld,%lld,%lld,%u,%d,%lld,%u,%d\n", #endif pDimmConfig->Socket, pDimmConfig->DeviceHandle, BYTES_TO_GIB(pDimmConfig->Capacity), BYTES_TO_GIB(pDimmConfig->VolatileSize), BYTES_TO_GIB(pDimmConfig->Persistent[0].PersistentSize), pDimmConfig->Persistent[FIRST_POOL_GOAL].InterleaveFormat.AsUint32, pDimmConfig->Persistent[FIRST_POOL_GOAL].PersistentIndex, BYTES_TO_GIB(pDimmConfig->Persistent[SECOND_POOL_GOAL].PersistentSize), pDimmConfig->Persistent[SECOND_POOL_GOAL].InterleaveFormat.AsUint32, pDimmConfig->Persistent[SECOND_POOL_GOAL].PersistentIndex); /** Save the line to file **/ ReturnCode = WriteAsciiLine(pFileHandle, pLineBuffer); if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("Failed in dump. DimmID 0x%04x.", pDimmConfig->pDimm->DeviceHandle.AsUint32); goto Finish; } } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pLineBuffer); return ReturnCode; } /** Set up pool goal structures to be loaded. @param[in] pDimms Array of Dimms @param[out] pDimmsConfig Array of Dimm Config @param[in] DimmsNum Number of elements in pDimms and pDimmsConfig @param[in] pFileString Contains Pool Goal Configuration @param[in, out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS Configuration is valid and loaded properly @retval EFI_INVALID_PARAMETER Invalid Parameter during load @retval other Return Codes from TrimLineBuffer, GetLoadPoolData, GetLoadDimmData, GetLoadValue functions **/ EFI_STATUS SetUpGoalStructures( IN DIMM *pDimms[], OUT DIMM_CONFIG DimmsConfig[], IN UINT32 DimmsNum, IN CHAR8 *pFileString, IN OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; UINT32 DimmIndex = 0; UINT32 DimmConfigIndex = 0; DIMM_CONFIG CurrentDimmConfig; BOOLEAN Found = FALSE; UINT32 NumberOfLines = 0; CHAR8 **ppLinesBuffer = NULL; NVDIMM_ENTRY(); SetMem(&CurrentDimmConfig, sizeof(CurrentDimmConfig), 0x0); if (DimmsConfig == NULL || pCommandStatus == NULL || pFileString == NULL) { NVDIMM_DBG("Invalid Pointer."); goto Finish; } // Split input file to lines (but ignore byte order mark) ppLinesBuffer = AsciiStrSplit(&pFileString[0], '\n', &NumberOfLines); if (ppLinesBuffer == NULL || NumberOfLines == 0) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } for (DimmIndex = 0; DimmIndex < DimmsNum; DimmIndex++) { Found = FALSE; for (Index = 1; Index < NumberOfLines; ++Index) { // Try to match line with Dimm Config ReturnCode = GetLoadDimmConfigData(ppLinesBuffer[Index], &CurrentDimmConfig); if (!EFI_ERROR(ReturnCode)) { if (pDimms[DimmIndex]->DeviceHandle.AsUint32 == CurrentDimmConfig.DeviceHandle.AsUint32 && pDimms[DimmIndex]->SocketId == CurrentDimmConfig.Socket) { CurrentDimmConfig.pDimm = pDimms[DimmIndex]; Found = TRUE; break; } } } if (Found) { if (DimmConfigIndex == DimmsNum) { NVDIMM_WARN("There are two or more the same Device Handle in the file."); ReturnCode = EFI_ABORTED; ResetCmdStatus(pCommandStatus, NVM_ERR_LOAD_INVALID_DATA_IN_FILE); goto Finish; } DimmsConfig[DimmConfigIndex] = CurrentDimmConfig; DimmConfigIndex++; } else { NVDIMM_WARN("Specified DIMM has no match in the file."); ReturnCode = EFI_ABORTED; ResetCmdStatus(pCommandStatus, NVM_ERR_LOAD_INVALID_DATA_IN_FILE); goto Finish; } } if (DimmConfigIndex == DimmsNum) { ReturnCode = EFI_SUCCESS; } else { NVDIMM_WARN("Not all specified DIMMs have match in the file."); ReturnCode = EFI_ABORTED; ResetCmdStatus(pCommandStatus, NVM_ERR_LOAD_DIMM_COUNT_MISMATCH); goto Finish; } Finish: for (Index = 0; ppLinesBuffer != NULL && Index < NumberOfLines; ++Index) { FREE_POOL_SAFE(ppLinesBuffer[Index]); } FREE_POOL_SAFE(ppLinesBuffer); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieves Dimm Config data from string @param[in] pDataLine Pointer to string with Config Dimm data @param[out] pDimmConfigData Pointer structure where to write retrieved data @retval EFI_SUCCESS Dimm data retrieved successfully @retval EFI_INVALID_PARAMETER Invalid Parameter @retval EFI_OUT_OF_RESOURCES Could not allocate memory **/ EFI_STATUS GetLoadDimmConfigData( IN CHAR8 *pDataLine, OUT DIMM_CONFIG *pDimmConfigData ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; UINT64 LineLength = 0; CHAR8 *pTrimmedLine = NULL; CHAR8 **ppTokenizedLine = NULL; UINT32 TokensCount = 0; UINT64 TmpValue = 0; NVDIMM_ENTRY(); if (pDataLine == NULL || pDimmConfigData == NULL) { NVDIMM_DBG("Invalid pointer"); goto Finish; } // Allocate memory pTrimmedLine = AllocateZeroPool(MAX_LINE_BYTE_LENGTH); if (pTrimmedLine == NULL) { NVDIMM_DBG("Failed to allocate memory."); ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } // Strip white characters LineLength = MAX_LINE_CHAR_LENGTH; ReturnCode = RemoveWhiteSpaces(pDataLine, pTrimmedLine, &LineLength); if (EFI_ERROR(ReturnCode) || LineLength == 0) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Skip comments if (pTrimmedLine[0] == '#') { NVDIMM_DBG("Comment skipped."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Split line into tokens by ',' ppTokenizedLine = AsciiStrSplit(pTrimmedLine, ',', &TokensCount); if (((TokensCount != NUMBER_OF_TOKENS_IN_DUMP_CONFIG_LINE_V1) && (TokensCount != NUMBER_OF_TOKENS_IN_DUMP_CONFIG_LINE_V2)) || ppTokenizedLine == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } //Iterate over all tokens for (Index = 0; Index < TokensCount; Index++) { TmpValue = AsciiStrDecimalToUint64(ppTokenizedLine[Index]); switch (Index) { case 0: // Socket pDimmConfigData->Socket = (UINT16) TmpValue; break; case 1:// DeviceHandle pDimmConfigData->DeviceHandle.AsUint32 = (UINT32) TmpValue; break; case 2:// Capacity pDimmConfigData->Capacity = (UINT64) TmpValue; break; case 3:// VolatileSize pDimmConfigData->VolatileSize = (UINT64) TmpValue; break; case 4:// Persistent1Size pDimmConfigData->Persistent[0].PersistentSize = (UINT64) TmpValue; break; case 5:// Persistent1Format pDimmConfigData->Persistent[0].InterleaveFormat.AsUint32 = (UINT32) TmpValue; break; case 6:// Persistent1Index pDimmConfigData->Persistent[0].PersistentIndex = (UINT16) TmpValue; break; case 7:// Persistent2Size pDimmConfigData->Persistent[1].PersistentSize = (UINT64) TmpValue; break; case 8:// Persistent2Format pDimmConfigData->Persistent[1].InterleaveFormat.AsUint32 = (UINT32) TmpValue; break; case 9:// Persistent2Index pDimmConfigData->Persistent[1].PersistentIndex = (UINT16) TmpValue; break; case 10:// LabelVersionMajor pDimmConfigData->LabelVersionMajor = (UINT16) TmpValue; break; case 11:// LabelVersionMinor pDimmConfigData->LabelVersionMinor = (UINT16) TmpValue; break; default: NVDIMM_DBG("Invalid number of separators ',' and fields in file."); goto Finish; } } // If using older dump without label versions, set to default 1.2 if (TokensCount == NUMBER_OF_TOKENS_IN_DUMP_CONFIG_LINE_V1) { pDimmConfigData->LabelVersionMajor = NSINDEX_MAJOR; pDimmConfigData->LabelVersionMinor = NSINDEX_MINOR_2; } ReturnCode = EFI_SUCCESS; Finish: for (Index = 0; ppTokenizedLine != NULL && Index < TokensCount; ++Index) { FREE_POOL_SAFE(ppTokenizedLine[Index]); } FREE_POOL_SAFE(ppTokenizedLine); FREE_POOL_SAFE(pTrimmedLine); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get array of current config from configured dimms. The caller is responsible for freeing of ppDimmConfigsArg. @param[out] ppDimmConfigsArg Pointer to output array of DIMM_CONFIG @param[out] pDimmConfigsNumArg Pointer to output number of DIMM_CONFIG items @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER one or more parameters are invalid **/ EFI_STATUS GetDimmsCurrentConfig( OUT DIMM_CONFIG **ppDimmConfigsArg, OUT UINT32 *pDimmConfigsNumArg ) { EFI_STATUS ReturnCode = EFI_SUCCESS; LIST_ENTRY *pDimmNode = NULL; DIMM *pDimm = NULL; LIST_ENTRY *pDimmRegionNode = NULL; DIMM_REGION *pDimmRegion = NULL; DIMM_CONFIG *pDimmConfigs = NULL; UINT32 DimmConfigsNum = 0; NVM_IS *pIS = NULL; NVM_IS *pISs[MAX_IS_CONFIGS]; UINT32 ISsNum = 0; BOOLEAN AssignedAlready = FALSE; UINT32 Index = 0; UINT32 Index2 = 0; UINT32 Index3 = 0; PERSISTENT_ENTRY PersistentTmp; BOOLEAN HasLatestLabelVersion = FALSE; NVDIMM_ENTRY(); ZeroMem(pISs, sizeof(pISs)); ZeroMem(&PersistentTmp, sizeof(PersistentTmp)); if (ppDimmConfigsArg == NULL || pDimmConfigsNumArg == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto FinishError; } /** Count a number of configured dimms **/ DimmConfigsNum = 0; LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (pDimm == NULL) { ReturnCode = EFI_ABORTED; goto FinishError; } if (!IsDimmManageable(pDimm)) { continue; } if (pDimm->Configured) { DimmConfigsNum++; } } if (DimmConfigsNum == 0) { *ppDimmConfigsArg = NULL; *pDimmConfigsNumArg = 0; goto Finish; } pDimmConfigs = (DIMM_CONFIG *) AllocateZeroPool(sizeof(*pDimmConfigs) * DimmConfigsNum); if (pDimmConfigs == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto FinishError; } Index = 0; LIST_FOR_EACH(pDimmNode, &gNvmDimmData->PMEMDev.Dimms) { pDimm = DIMM_FROM_NODE(pDimmNode); if (pDimm == NULL) { ReturnCode = EFI_ABORTED; goto FinishError; } if (!IsDimmManageable(pDimm) || !pDimm->Configured) { continue; } pDimmConfigs[Index].pDimm = pDimm; pDimmConfigs[Index].Socket = pDimm->SocketId; pDimmConfigs[Index].DeviceHandle = pDimm->DeviceHandle; pDimmConfigs[Index].Capacity = pDimm->RawCapacity; pDimmConfigs[Index].VolatileSize = pDimm->VolatileCapacity; ReturnCode = CheckDimmNsLabelVersion(pDimm, &HasLatestLabelVersion); // If not ns label version, set it to default 1.2 if (EFI_ERROR(ReturnCode) || (HasLatestLabelVersion)) { pDimmConfigs[Index].LabelVersionMajor = NSINDEX_MAJOR; pDimmConfigs[Index].LabelVersionMinor = NSINDEX_MINOR_2; } else { pDimmConfigs[Index].LabelVersionMajor = NSINDEX_MAJOR; pDimmConfigs[Index].LabelVersionMinor = NSINDEX_MINOR_1; } for (Index2 = 0; Index2 < pDimm->ISsNum; Index2++) { pIS = pDimm->pISs[Index2]; LIST_FOR_EACH(pDimmRegionNode, &pIS->DimmRegionList) { pDimmRegion = DIMM_REGION_FROM_NODE(pDimmRegionNode); if (pDimm->SerialNumber == pDimmRegion->pDimm->SerialNumber) { pDimmConfigs[Index].Persistent[Index2].PersistentSize = pDimmRegion->PartitionSize; } } ASSERT(pDimmConfigs[Index].Persistent[Index2].PersistentSize != 0); pDimmConfigs[Index].Persistent[Index2].InterleaveFormat.InterleaveFormatSplit.iMCInterleaveSize = pIS->InterleaveFormatImc; pDimmConfigs[Index].Persistent[Index2].InterleaveFormat.InterleaveFormatSplit.ChannelInterleaveSize = pIS->InterleaveFormatChannel; pDimmConfigs[Index].Persistent[Index2].InterleaveFormat.InterleaveFormatSplit.NumberOfChannelWays = pIS->InterleaveFormatWays; /** Index of interleave sets assigning **/ AssignedAlready = FALSE; for (Index3 = 0; Index3 < ISsNum; Index3++) { if (pISs[Index3] == pIS) { AssignedAlready = TRUE; break; } } if (AssignedAlready) { pDimmConfigs[Index].Persistent[Index2].PersistentIndex = (UINT16)(Index3 + 1); } else { /** Assign new index. **/ pISs[ISsNum] = pIS; ISsNum++; pDimmConfigs[Index].Persistent[Index2].PersistentIndex = (UINT16)ISsNum; } } Index++; } /** Adjust position of persistent entries of the same interleave set to set them the same index in array. It causes that one interleave set will be in the same column in the dump file. Applicable only for more than one interleave set request. **/ if (ISsNum > 1) { for (Index = 0; Index < DimmConfigsNum; Index++) { for (Index2 = Index + 1; Index2 < DimmConfigsNum; Index2++) { if (((pDimmConfigs[Index].Persistent[0].PersistentIndex == pDimmConfigs[Index2].Persistent[1].PersistentIndex) && (pDimmConfigs[Index].Persistent[0].PersistentIndex != 0)) || ((pDimmConfigs[Index].Persistent[1].PersistentIndex == pDimmConfigs[Index2].Persistent[0].PersistentIndex) && (pDimmConfigs[Index].Persistent[1].PersistentIndex != 0))) { PersistentTmp = pDimmConfigs[Index2].Persistent[1]; pDimmConfigs[Index2].Persistent[1] = pDimmConfigs[Index2].Persistent[0]; pDimmConfigs[Index2].Persistent[0] = PersistentTmp; } } } } /** if everything went successfully, assign data to output variables **/ *ppDimmConfigsArg = pDimmConfigs; *pDimmConfigsNumArg = DimmConfigsNum; goto Finish; FinishError: if (ppDimmConfigsArg != NULL) { FREE_POOL_SAFE(*ppDimmConfigsArg); } if (pDimmConfigsNumArg != NULL) { *pDimmConfigsNumArg = 0; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Verify config loaded from file and prepare goal config parameters for create goal config function @param[in] Socket Socket ID @param[in] DimmsConfig Array of Dimm Config @param[in] DimmsConfigNum Number of elements in DimmsConfig @param[out] DimmIds Array of DIMM IDs @param[out] pDimmIdsNum Number of items in array of DIMM IDs @param[out] pPersistentMemType Persistent memory type @param[out] pVolatilePercent Volatile region size in percents @param[out] pReservedPercent Reserved region size in percents @param[out] pLabelVersionMajor Label Major Version to use @param[out] pLabelVersionMinor Label Minor Version to use @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER one or more parameters are invalid @retval EFI_ABORTED improper configuration **/ EFI_STATUS ValidateAndPrepareLoadConfig( IN UINT16 Socket, IN DIMM_CONFIG DimmsConfig[], IN UINT32 DimmsConfigNum, OUT UINT16 DimmIds[MAX_DIMMS_PER_SOCKET], OUT UINT32 *pDimmIdsNum, OUT UINT8 *pPersistentMemType, OUT UINT32 *pVolatilePercent, OUT UINT32 *pReservedPercent, OUT UINT16 *pLabelVersionMajor, OUT UINT16 *pLabelVersionMinor, OUT COMMAND_STATUS *pCommandStatus ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; UINT32 Index = 0; UINT32 Index2 = 0; DIMM_CONFIG *pDimmsConfigOnSocket[MAX_DIMMS_PER_SOCKET]; UINT32 SpecifiedDimmsOnSocketNum = 0; UINT64 DimmsCapacity = 0; UINT64 VolatileCapacity = 0; UINT64 ReservedCapacity = 0; BOOLEAN Reserved = TRUE; BOOLEAN AppDirectInterleaved = FALSE; UINT16 TempMajor = 0; UINT16 TempMinor = 0; BOOLEAN UseDefaultLabel = FALSE; NVDIMM_ENTRY(); ZeroMem(pDimmsConfigOnSocket, sizeof(pDimmsConfigOnSocket)); if (DimmsConfig == NULL || DimmIds == NULL || pDimmIdsNum == NULL || pPersistentMemType == NULL || pVolatilePercent == NULL || pCommandStatus == NULL) { goto Finish; } for (Index = 0, SpecifiedDimmsOnSocketNum = 0; Index < DimmsConfigNum; Index++) { if (DimmsConfig[Index].pDimm->SocketId == Socket) { ASSERT(SpecifiedDimmsOnSocketNum < MAX_DIMMS_PER_SOCKET); /** Limited dimms config only to specified socket's dimms **/ pDimmsConfigOnSocket[SpecifiedDimmsOnSocketNum] = &DimmsConfig[Index]; /** Preparing list of dimm IDs for create goal command **/ DimmIds[SpecifiedDimmsOnSocketNum] = DimmsConfig[Index].pDimm->DimmID; SpecifiedDimmsOnSocketNum++; } } if (SpecifiedDimmsOnSocketNum > MAX_DIMMS_PER_SOCKET) { // sanity check ReturnCode = EFI_ABORTED; goto FinishClean; } if (SpecifiedDimmsOnSocketNum > 0) { for (Index = 0; Index < SpecifiedDimmsOnSocketNum && Reserved; Index++) { DimmsCapacity += pDimmsConfigOnSocket[Index]->Capacity; VolatileCapacity += pDimmsConfigOnSocket[Index]->VolatileSize; if (pDimmsConfigOnSocket[Index]->Capacity > pDimmsConfigOnSocket[Index]->VolatileSize + pDimmsConfigOnSocket[Index]->Persistent[0].PersistentSize + pDimmsConfigOnSocket[Index]->Persistent[1].PersistentSize) { ReservedCapacity += pDimmsConfigOnSocket[Index]->Capacity - pDimmsConfigOnSocket[Index]->VolatileSize - pDimmsConfigOnSocket[Index]->Persistent[0].PersistentSize - pDimmsConfigOnSocket[Index]->Persistent[1].PersistentSize; } if (pDimmsConfigOnSocket[Index]->Persistent[0].PersistentSize > 0) { Reserved = FALSE; } for (Index2 = 0; Index2 < SpecifiedDimmsOnSocketNum && !AppDirectInterleaved; Index2++) { if (Index == Index2) { continue; } if (pDimmsConfigOnSocket[Index]->Persistent[0].PersistentIndex == pDimmsConfigOnSocket[Index2]->Persistent[0].PersistentIndex) { AppDirectInterleaved = TRUE; } } } if (DimmsCapacity == 0 || VolatileCapacity > DimmsCapacity) { ReturnCode = EFI_ABORTED; ResetCmdStatus(pCommandStatus, NVM_ERR_LOAD_IMPROPER_CONFIG_IN_FILE); goto FinishClean; } else { *pVolatilePercent = (UINT32) (ROUND_CLOSEST((VolatileCapacity * 100), DimmsCapacity)); *pReservedPercent = (UINT32) (ROUND_CLOSEST((ReservedCapacity * 100), DimmsCapacity)); } if (Reserved) { *pPersistentMemType = PM_TYPE_RESERVED; } else if (AppDirectInterleaved) { *pPersistentMemType = PM_TYPE_AD; } else { *pPersistentMemType = PM_TYPE_AD_NI; } } else { /** No DIMMs specified on that socket, so it is also successful path. **/ } /** Label Version sanity check. Use default version if there are invalid inputs **/ TempMajor = DimmsConfig[0].LabelVersionMajor; TempMinor = DimmsConfig[0].LabelVersionMinor; if ((TempMajor != NSINDEX_MAJOR) || ((TempMinor != NSINDEX_MINOR_1) && (TempMinor != NSINDEX_MINOR_2))) { UseDefaultLabel = TRUE; } /** If versions on dimms don't match **/ for (Index = 1; Index < DimmsConfigNum; Index++) { if ((DimmsConfig[Index].LabelVersionMajor != TempMajor) && (DimmsConfig[Index].LabelVersionMinor != TempMinor)) { UseDefaultLabel = TRUE; } } if (UseDefaultLabel) { *pLabelVersionMajor = NSINDEX_MAJOR; *pLabelVersionMinor = NSINDEX_MINOR_2; } else { *pLabelVersionMajor = TempMajor; *pLabelVersionMinor = TempMinor; } *pDimmIdsNum = SpecifiedDimmsOnSocketNum; ReturnCode = EFI_SUCCESS; goto Finish; FinishClean: /** Clean all set data **/ ZeroMem(DimmIds, sizeof(DimmIds[0]) * MAX_DIMMS_PER_SOCKET); *pDimmIdsNum = 0; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/driver/Utils/DumpLoadRegions.h000066400000000000000000000113071440615110200227420ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DUMP_LOAD_REGIONS_H_ #define _DUMP_LOAD_REGIONS_H_ #include "Uefi.h" #include #include #include #define NUMBER_OF_TOKENS_IN_DUMP_CONFIG_LINE_V1 10 #define NUMBER_OF_TOKENS_IN_DUMP_CONFIG_LINE_V2 12 enum DumpFilePrintCodes { DumpFileBom, DumpVersion, DumpDimmConfigDescription, DumpSeparation, DumpNewLine, DumpLastNotUsed }; typedef struct _PERSISTENT_ENTRY { UINT64 PersistentSize; INTERLEAVE_FORMAT InterleaveFormat; UINT16 PersistentIndex; } PERSISTENT_ENTRY; typedef struct _DIMM_CONFIG { DIMM *pDimm; UINT16 Socket; NfitDeviceHandle DeviceHandle; UINT64 Capacity; UINT64 VolatileSize; PERSISTENT_ENTRY Persistent[MAX_IS_PER_DIMM]; UINT16 LabelVersionMajor; UINT16 LabelVersionMinor; } DIMM_CONFIG; /** Write dump Pool Goal Configuration header to file @param[in] FileHandle File handle to write to @retval EFI_SUCCESS Header written. @retval other Error codes from Write function. **/ EFI_STATUS WriteDumpFileHeader( IN EFI_FILE_HANDLE FileHandle ); /** Dump Pool Goal Configurations into file @param[in] pFileHandle File handler @param[in] DimmConfigs Array of DIMM_CONFIG @param[in] DimmConfigsNum Size of DimmConfigs array @retval EFI_SUCCESS Pool Goal Configuration dump successful @retval EFI_INVALID_PARAMETER Invalid Parameter during dump @retval EFI_OUT_OF_RESOURCES Not enough memory to dump @retval EFI_NO_MAPPING Wrong Pool Goal Configuration status **/ EFI_STATUS DumpConfigToFile( IN EFI_FILE_HANDLE pFileHandle, IN DIMM_CONFIG DimmConfigs[], IN UINT32 DimmConfigsNum ); /** Set up pool goal structures to be loaded. @param[in] pDimms Array of Dimms @param[out] pDimmsConfig Array of Dimm Config @param[in] DimmsNum Number of elements in pDimms and pDimmsConfig @param[in] pFileString Contains Pool Goal Configuration @param[in, out] pCommandStatus Pointer to command status structure @retval EFI_SUCCESS Configuration is valid and loaded properly @retval EFI_INVALID_PARAMETER Invalid Parameter during load @retval other Return Codes from TrimLineBuffer, GetLoadPoolData, GetLoadDimmData, GetLoadValue functions **/ EFI_STATUS SetUpGoalStructures( IN DIMM *pDimms[], OUT DIMM_CONFIG DimmsConfig[], IN UINT32 DimmsNum, IN CHAR8 *pFileString, IN OUT COMMAND_STATUS *pCommandStatus ); /** Retrieves Dimm Config data from string @param[in] pDataLine Pointer to string with Config Dimm data @param[out] pDimmConfigData Pointer structure where to write retrieved data @retval EFI_SUCCESS Dimm data retrieved successfully @retval EFI_INVALID_PARAMETER Invalid Parameter @retval EFI_OUT_OF_RESOURCES Could not allocate memory **/ EFI_STATUS GetLoadDimmConfigData( IN CHAR8 *pDataLine, OUT DIMM_CONFIG *pDimmConfigData ); /** Get array of current config from configured dimms. The caller is responsible for freeing of ppDimmConfigsArg. @param[out] ppDimmConfigsArg Pointer to output array of DIMM_CONFIG @param[out] pDimmConfigsNumArg Pointer to output number of DIMM_CONFIG items @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER one or more parameters are invalid **/ EFI_STATUS GetDimmsCurrentConfig( OUT DIMM_CONFIG **ppDimmConfigsArg, OUT UINT32 *pDimmConfigsNumArg ); /** Verify config loaded from file and prepare goal config parameters for create goal config function @param[in] Socket Socket ID @param[in] DimmsConfig Array of Dimm Config @param[in] DimmsConfigNum Number of elements in DimmsConfig @param[out] DimmIds Array of DIMM IDs @param[out] pDimmIdsNum Number of items in array of DIMM IDs @param[out] pPersistentMemType Persistent memory type @param[out] pVolatilePercent Volatile region size in percents @param[out] pReservedPercent Reserved region size in percents @param[out] pLabelVersionMajor Label Major Version to use @param[out] pLabelVersionMinor Label Minor Version to use @param[out] pCommandStatus Structure containing detailed NVM error codes @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER one or more parameters are invalid @retval EFI_ABORTED improper configuration **/ EFI_STATUS ValidateAndPrepareLoadConfig( IN UINT16 Socket, IN DIMM_CONFIG DimmsConfig[], IN UINT32 DimmsConfigNum, OUT UINT16 DimmIds[MAX_DIMMS_PER_SOCKET], OUT UINT32 *pDimmIdsNum, OUT UINT8 *pPersistentMemType, OUT UINT32 *pVolatilePercent, OUT UINT32 *pReservedPercent, OUT UINT16 *pLabelVersionMajor, OUT UINT16 *pLabelVersionMinor, OUT COMMAND_STATUS *pCommandStatus ); #endif /** _DUMP_LOAD_REGIONS_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/driver/Utils/Interleave.c000066400000000000000000000216041440615110200220000ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "Dimm.h" #include "Interleave.h" #include "Namespace.h" #include "Region.h" #include #define DIMM_LOCATION(iMC, channel, iMCsPerCPU, channelsPeriMC) (channelsPeriMC * (iMC % iMCsPerCPU) + channel) #define DIMM_POPULATED(map, dimmIndex) ((map >> dimmIndex) & 0x1) #define CLEAR_DIMM(map, dimmIndex) map &= ~(0x1 << dimmIndex) /** Remove the dimm from the list of dimms and update the other dimms in the list to shift left by 1. @param[in out] pDimmsList The current list of DIMMs @param[in out] pDimmsListNum The number of DIMMs @param[in out] pDimm The DIMM to be removed from pDimmsList @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER If input parameters are null **/ STATIC EFI_STATUS RemoveDimmFromList( IN OUT DIMM *pDimmsList[], IN OUT UINT32 *pDimmsListNum, IN DIMM *pDimm ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Index = 0; BOOLEAN Found = FALSE; NVDIMM_ENTRY(); if (pDimmsList == NULL || pDimmsListNum == NULL || pDimm == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } for (Index = 0; Index < *pDimmsListNum; Index++) { if (pDimm == pDimmsList[Index]) { Found = TRUE; break; } } if (Found) { for (; Index < (*pDimmsListNum - 1); Index++) { pDimmsList[Index] = pDimmsList[Index + 1]; } (*pDimmsListNum)--; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Remove the dimm array from the list of dimms and update the number of dimms in the list. @param[in out] pDimmsList The current list of DIMMs @param[in out] pDimmsListNum The number of DIMMs @param[in out] pDimmsArray The DIMMs to be removed from pDimmsList @param[in] DimmsArrayNum Number of DIMMs to be removed @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER If input parameters are null **/ STATIC EFI_STATUS RemoveDimmArrayFromList( IN OUT DIMM *pDimmsList[], IN OUT UINT32 *pDimmsListNum, IN OUT DIMM *pDimmsArray[], IN UINT32 DimmsArrayNum ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Index = 0; NVDIMM_ENTRY(); if (pDimmsList == NULL || pDimmsArray == NULL || pDimmsListNum == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } for (Index = 0; Index < DimmsArrayNum; Index++) { // Remove 1 Dimm from the list at a time RemoveDimmFromList(pDimmsList, pDimmsListNum, pDimmsArray[Index]); } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get the DIMMs matching the current interleave set map. @param[in out] pDimms The current list of DIMMs @param[in] DimmsNum The number of DIMMs @param[in] InterleaveMap The current interleave map @param[in out] pDimmsInterleaved The DIMMs to be interleaved together @param[in out] pDimmsUsed Number of interleaved DIMMs @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER If input parameters are null **/ STATIC EFI_STATUS GetDimmsFromListMatchingInterleaveSet( IN DIMM *pDimms[], IN UINT32 DimmsNum, IN const UINT32 InterleaveMap, IN UINT8 iMCNum, IN UINT8 ChannelNum, IN OUT DIMM *pDimmsInterleaved[], IN OUT UINT32 *pDimmsUsed ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 DimmsNotFound = InterleaveMap; UINT32 Index = 0; UINT32 DimmIndex = 0; NVDIMM_ENTRY(); if (pDimms == NULL || pDimmsInterleaved == NULL || pDimmsUsed == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } NVDIMM_DBG("InterleaveMap flags = %lu", InterleaveMap); for (Index = 0; Index < DimmsNum; Index++) { DimmIndex = DIMM_LOCATION(pDimms[Index]->ImcId, pDimms[Index]->ChannelId, iMCNum, ChannelNum); NVDIMM_DBG("DIMM %d ImcId = %lu", Index, pDimms[Index]->ImcId); NVDIMM_DBG("DIMM %d ChannelId = %lu", Index, pDimms[Index]->ChannelId); NVDIMM_DBG("DIMM %d Map Location = %lu", Index, DimmIndex); if (DIMM_POPULATED(InterleaveMap, DimmIndex)) { pDimmsInterleaved[(*pDimmsUsed)++] = pDimms[Index]; CLEAR_DIMM(DimmsNotFound, DimmIndex); } } NVDIMM_DBG("InterleaveMap flags without located DIMMs = %lu", DimmsNotFound); if (DimmsNotFound != 0) { *pDimmsUsed = 0; NVDIMM_DBG("Not all the required map position flags were matched to DIMMs. Condition not satisfied."); } else if(*pDimmsUsed > 0) { NVDIMM_DBG("All the required map position flags were matched to DIMMs. Condition satisfied!"); } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Find the best possible set of DIMMs that can be interleaved. @param[in out] pDimms The current list of DIMMs @param[in] DimmsNum The number of DIMMs @param[in out] pDimmsInterleaved The DIMMs to be interleaved together @param[in out] pDimmsUsed Number of interleaved DIMMs @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER If input parameters are null **/ STATIC EFI_STATUS FindBestInterleavingForDimms( IN OUT DIMM *pDimms[], IN UINT32 DimmsNum, IN OUT DIMM *pDimmsInterleaved[], IN OUT UINT32 *pDimmsUsed ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 Index = 0; UINT8 NumOfiMCsPerCPU = 0; UINT8 NumOfChannelsPeriMC = 0; UINT32 *pInterleaveSet = NULL; NVDIMM_ENTRY(); if (pDimms == NULL || pDimmsInterleaved == NULL || pDimmsUsed == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = GetTopologyAndInterleaveSetMapInfo(&NumOfiMCsPerCPU, &NumOfChannelsPeriMC, &pInterleaveSet); if (EFI_ERROR(ReturnCode)) { goto Finish; } for (Index = 0; pInterleaveSet[Index] != END_OF_INTERLEAVE_SETS; Index++) { NVDIMM_DBG("Checking interleave set map %d (%lu)", Index, pInterleaveSet[Index]); NVDIMM_DBG("NumOfiMCsPerCPU = %lu", NumOfiMCsPerCPU); NVDIMM_DBG("NumOfChannelsPeriMC = %lu", NumOfChannelsPeriMC); GetDimmsFromListMatchingInterleaveSet(pDimms, DimmsNum, pInterleaveSet[Index], NumOfiMCsPerCPU, NumOfChannelsPeriMC, pDimmsInterleaved, pDimmsUsed); if (*pDimmsUsed > 0) { NVDIMM_DBG("Found interleave map match"); break; } } if (*pDimmsUsed == 0) { NVDIMM_WARN("Interleaving map match not found"); ReturnCode = EFI_ABORTED; goto Finish; } Finish: FREE_POOL_SAFE(pInterleaveSet); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Perform interleaving across DIMMs and create goals @param[in] pRegionGoalTemplate The current pool goal template @param[in] pDimms The list of DIMMs @param[in] DimmsNum The number of DIMMs in the list @param[in] InterleaveSetSize The size of the interleave set @param[in] pDriverPreferences The driver preferences @param[in] SequenceIndex Sequence index per pool goal template @param[in out] pRegionGoal The list of pool goals @param[in out] pNewRegionsGoalNum Number of new pool goals @param[in out] pInterleaveSetIndex The interleave set index of the goal @retval EFI_SUCCESS #retval EFI_INVALID_PARAMETER If IS is null **/ EFI_STATUS PerformInterleavingAndCreateGoal( IN REGION_GOAL_TEMPLATE *pRegionGoalTemplate, IN DIMM *pDimms[], IN UINT32 DimmsNum, IN UINT64 InterleaveSetSize, IN DRIVER_PREFERENCES *pDriverPreferences OPTIONAL, IN UINT16 SequenceIndex, IN OUT struct _REGION_GOAL *pRegionGoal[], IN OUT UINT32 *pNewRegionsGoalNum, IN OUT UINT16 *pInterleaveSetIndex ) { EFI_STATUS ReturnCode = EFI_SUCCESS; DIMM *pDimmsCopy[MAX_DIMMS]; DIMM *pDimmsInterleaved[MAX_DIMMS]; UINT32 RemainingDimms = DimmsNum; UINT32 DimmsUsed = 0; UINT32 TotalDimmsUsed = 0; NVDIMM_ENTRY(); ZeroMem(pDimmsCopy, sizeof(pDimmsCopy)); ZeroMem(pDimmsInterleaved, sizeof(pDimmsInterleaved)); if (pRegionGoalTemplate == NULL || pRegionGoal == NULL || pNewRegionsGoalNum == NULL || pDimms == NULL || pInterleaveSetIndex == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } // Goal cannot be created of size Zero if (InterleaveSetSize == 0) { goto Finish; } if (DimmsNum > 0) { CopyMem_S(pDimmsCopy, sizeof(pDimms[0]) * DimmsNum, pDimms, sizeof(pDimms[0]) * DimmsNum); } while (RemainingDimms > 0) { ReturnCode = FindBestInterleavingForDimms(pDimmsCopy, RemainingDimms, pDimmsInterleaved, &DimmsUsed); if (EFI_ERROR(ReturnCode)) { goto Finish; } RemoveDimmArrayFromList(pDimmsCopy, &RemainingDimms, pDimmsInterleaved, DimmsUsed); TotalDimmsUsed += DimmsUsed; if (TotalDimmsUsed > DimmsNum) { ReturnCode = EFI_BAD_BUFFER_SIZE; goto Finish; } pRegionGoal[(*pNewRegionsGoalNum)] = CreateRegionGoal(pRegionGoalTemplate, pDimmsInterleaved, DimmsUsed, InterleaveSetSize * DimmsUsed / DimmsNum, pDriverPreferences, (UINT16)SequenceIndex, pInterleaveSetIndex); if (pRegionGoal[(*pNewRegionsGoalNum)] == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; goto Finish; } (*pNewRegionsGoalNum)++; DimmsUsed = 0; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/driver/Utils/Interleave.h000066400000000000000000000030341440615110200220020ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _INTERLEAVE_H_ #define _INTERLEAVE_H_ #include #include #include #include #include #include #include #include "ProcessorAndTopologyInfo.h" #ifdef OS_BUILD #include #endif // OS_BUILD /** Perform interleaving across DIMMs and create goals @param[in] pPoolGoalTemplate The current pool goal template @param[in] pDimms The list of DIMMs @param[in] DimmsNum The number of DIMMs in the list @param[in] InterleaveSetSize The size of the interleave set @param[in] pDriverPreferences The driver preferences @param[in] SequenceIndex Sequence index per pool goal template @param[in out] pPoolGoal The list of pool goals @param[in out] pNewPoolsGoalNum Number of new pool goals @param[in out] pInterleaveSetIndex The interleave set index of the goal @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER If input parameters are null @retval EFI_OUT_OF_RESOURCES if allocation of new goal fails **/ EFI_STATUS PerformInterleavingAndCreateGoal( IN REGION_GOAL_TEMPLATE *pPoolGoalTemplate, IN DIMM *pDimms[], IN UINT32 DimmsNum, IN UINT64 InterleaveSetSize, IN DRIVER_PREFERENCES *pDriverPreferences OPTIONAL, IN UINT16 SequenceIndex, IN OUT struct _REGION_GOAL *pRegionGoal[], IN OUT UINT32 *pNewPoolsGoalNum, IN OUT UINT16 *pInterleaveSetIndex ); #endif /** _INTERLEAVE_H_ **/ ipmctl-03.00.00.0485/DcpmPkg/driver/Utils/PlatformConfigData.c000066400000000000000000000645661440615110200234240ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "PlatformConfigData.h" #include #include #include #include #include #define NUM_OF_DIMMS_IN_SIX_WAY_INTERLEAVE_SET 6 extern NVMDIMMDRIVER_DATA *gNvmDimmData; /** Generate PCD Configuration Input for the dimm based on its pools goal configuration The caller is responsible to free the allocated memory of PCD Config Input @param[in] pDimm the dimm that PCD Config Input is destined for @param[in] ReservedSizeIsZero Indicate whether the reserved size is zero @param[out] ppConfigInput new generated PCD Config Input @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS GeneratePcdConfInput( IN struct _DIMM *pDimm, IN BOOLEAN ReservedSizeIsZero, OUT NVDIMM_PLATFORM_CONFIG_INPUT **ppConfigInput ) { EFI_STATUS Rc = EFI_SUCCESS; VOID *pCurrentOffset = NULL; ACPI_REVISION Revision; NVDIMM_PARTITION_SIZE_CHANGE *pPartSizeChange = NULL; CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *pAutoProvExtension = NULL; UINT32 ConfInputSize = 0; UINT64 LastPersistentMemoryOffset = 0; UINT32 Index = 0; UINT32 Index2 = 0; NVDIMM_CONFIGURATION_HEADER *pConfHeader = NULL; UINT64 PmPartitionSize = 0; INTEL_DIMM_CONFIG *pIntelDIMMConfigEfiVar = NULL; INTEL_DIMM_CONFIG *pIntelDIMMConfigIn = NULL; PMTT_MODULE_INFO *pPmttModuleInfo = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || ppConfigInput == NULL) { Rc = EFI_INVALID_PARAMETER; goto Finish; } /** Populate their revision of the CIN table **/ Rc = GetPlatformConfigDataOemPartition(pDimm, FALSE, &pConfHeader); #ifdef MEMORY_CORRUPTION_WA if (Rc == EFI_DEVICE_ERROR) { Rc = GetPlatformConfigDataOemPartition(pDimm, FALSE, &pConfHeader); } #endif // MEMORY_CORRUPTIO_WA if (EFI_ERROR(Rc)) { NVDIMM_DBG("Error in retrieving the Current Config table"); goto Finish; } if (gNvmDimmData->PMEMDev.pPcatHead == NULL) { NVDIMM_DBG("PCAT table not found"); Rc = EFI_DEVICE_ERROR; goto Finish; } else if (IS_ACPI_HEADER_REV_INVALID(gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr)) { NVDIMM_DBG("Incorrect PCAT table"); Rc = EFI_DEVICE_ERROR; goto Finish; } else { Revision = gNvmDimmData->PMEMDev.pPcatHead->pPlatformConfigAttr->Header.Revision; } /** Allocate the block of memory for PCD Config Input **/ if (IS_ACPI_REV_MAJ_0_MIN_VALID(Revision)) { ConfInputSize = sizeof(NVDIMM_PLATFORM_CONFIG_INPUT) + sizeof(NVDIMM_PARTITION_SIZE_CHANGE) + pDimm->RegionsGoalNum * (sizeof(NVDIMM_INTERLEAVE_INFORMATION)); for (Index = 0; Index < pDimm->RegionsGoalNum; Index++) { ConfInputSize += pDimm->pRegionsGoal[Index]->DimmsNum * sizeof(NVDIMM_IDENTIFICATION_INFORMATION); } } else if (IS_ACPI_REV_MAJ_1_OR_MAJ_3(Revision)) { ConfInputSize = sizeof(NVDIMM_PLATFORM_CONFIG_INPUT) + sizeof(NVDIMM_PARTITION_SIZE_CHANGE) + pDimm->RegionsGoalNum * (sizeof(NVDIMM_INTERLEAVE_INFORMATION3)); for (Index = 0; Index < pDimm->RegionsGoalNum; Index++) { ConfInputSize += pDimm->pRegionsGoal[Index]->DimmsNum * sizeof(NVDIMM_IDENTIFICATION_INFORMATION3); } } // Retrieve automatic provisioning EFI vars Rc = RetrieveIntelDIMMConfig(&pIntelDIMMConfigEfiVar); if (EFI_ERROR(Rc)) { // Not found or some error // Either case should not stop sending CIN Rc = EFI_SUCCESS; } else { if (pIntelDIMMConfigEfiVar->ProvisionCapacityMode == PROVISION_CAPACITY_MODE_AUTO) { // Add extension table to size ConfInputSize += sizeof(CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE) + sizeof(INTEL_DIMM_CONFIG); } } *ppConfigInput = AllocateZeroPool(ConfInputSize); if (*ppConfigInput == NULL) { Rc = EFI_OUT_OF_RESOURCES; goto Finish; } /** PCD Configuration Input table **/ (*ppConfigInput)->Header.Signature = NVDIMM_CONFIGURATION_INPUT_SIG; (*ppConfigInput)->Header.Length = ConfInputSize; (*ppConfigInput)->Header.Revision = Revision; CopyMem_S((*ppConfigInput)->Header.OemId, sizeof((*ppConfigInput)->Header.OemId), pConfHeader->Header.OemId, sizeof((*ppConfigInput)->Header.OemId)); (*ppConfigInput)->Header.OemTableId = pConfHeader->Header.OemTableId; (*ppConfigInput)->Header.OemRevision = pConfHeader->Header.OemRevision; (*ppConfigInput)->Header.CreatorId = pConfHeader->Header.CreatorId; (*ppConfigInput)->Header.CreatorRevision = pConfHeader->Header.CreatorRevision; /** We read the last SequenceNumber from Config Output table and increase it by 1. If it will be max UINT32 (2^32 - 1) value, then after increasing we will get 0, what is fine. **/ Rc = GetNewSequenceNumber(pDimm, &(*ppConfigInput)->SequenceNumber); if (EFI_ERROR(Rc)) { goto Finish; } /** Partition Size Change table **/ pPartSizeChange = (NVDIMM_PARTITION_SIZE_CHANGE *) &(*ppConfigInput)->pPcatTables; pPartSizeChange->Header.Type = PCAT_TYPE_PARTITION_SIZE_CHANGE_TABLE; pPartSizeChange->Header.Length = sizeof(NVDIMM_PARTITION_SIZE_CHANGE); /** Dimm Size = PM Size + Volatile Size User specifies Volatile Size, but Partition Size Change table needs PartitionSize defines size of persistent memory So here we specify the Persistent Size Aligned to 32GB. But if there is 0 Volatile, we will end up with Persistent Partition with a size under aligned by the reserved regions (cause the DIMM Raw capacity has them subtracted). The BIOS team has no problems with this value being under aligned right now, but they might change this in the future. If they do we will have to align this value properly. For FW version 3, attempt to match persistent memory partition size change request to the size of requested persistent memory for this PMem module, instead of using the raw PMem module size. That is done further below. However, this algorithm doesn't work that well if the user requested to keep some PMem memory unused ("reserved"). So use the old algorithm if this is the case, done here. **/ if (pDimm->FwVer.FwApiMajor < 3 || !ReservedSizeIsZero) { pPartSizeChange->PmPartitionSize = pDimm->RawCapacity - pDimm->VolatileSizeGoal; } // Set PmPartitionSize for FW API >= 3 further below based on LastPersistentMemoryOffset pPartSizeChange->PartitionSizeChangeStatus = NVDIMM_CONF_INPUT_PART_SIZE_CHANGE_STATUS; /** Interleave Information tables **/ pCurrentOffset = (UINT8 *)pPartSizeChange + sizeof(NVDIMM_PARTITION_SIZE_CHANGE); if (IS_ACPI_HEADER_REV_MAJ_0_MIN_VALID((*ppConfigInput))) { for (Index = 0; Index < pDimm->RegionsGoalNum; Index++) { NVDIMM_INTERLEAVE_INFORMATION *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION *)pCurrentOffset; pInterleaveInfo->Header.Type = PCAT_TYPE_INTERLEAVE_INFORMATION_TABLE; pInterleaveInfo->Header.Length = (UINT16)sizeof(NVDIMM_INTERLEAVE_INFORMATION) + (UINT16)pDimm->pRegionsGoal[Index]->DimmsNum * (UINT16)sizeof(NVDIMM_IDENTIFICATION_INFORMATION); pInterleaveInfo->InterleaveSetIndex = pDimm->pRegionsGoal[Index]->InterleaveSetIndex; pInterleaveInfo->NumOfDimmsInInterleaveSet = (UINT8)pDimm->pRegionsGoal[Index]->DimmsNum; pInterleaveInfo->InterleaveMemoryType = NVDIMM_MEMORY_PERSISTENT_TYPE; pInterleaveInfo->InterleaveFormatChannel = pDimm->pRegionsGoal[Index]->ChannelInterleaving; pInterleaveInfo->InterleaveFormatImc = pDimm->pRegionsGoal[Index]->ImcInterleaving; pInterleaveInfo->InterleaveFormatWays = pDimm->pRegionsGoal[Index]->NumOfChannelWays; pInterleaveInfo->InterleaveChangeStatus = 0; // Used by Config Output, 0 for Config Input pCurrentOffset = (UINT8 *)pCurrentOffset + sizeof(NVDIMM_INTERLEAVE_INFORMATION); /** Identification Information tables **/ PmPartitionSize = pDimm->pRegionsGoal[Index]->Size / pDimm->pRegionsGoal[Index]->DimmsNum; /** Sort Dimms list according to BIOS requirement for Dimms order in interleave set **/ if (pDimm->pRegionsGoal[Index]->DimmsNum == NUM_OF_DIMMS_IN_SIX_WAY_INTERLEAVE_SET) { Rc = BubbleSort(pDimm->pRegionsGoal[Index]->pDimms, pDimm->pRegionsGoal[Index]->DimmsNum, sizeof(DIMM *), CompareDimmOrderInInterleaveSet6Way); } else { Rc = BubbleSort(pDimm->pRegionsGoal[Index]->pDimms, pDimm->pRegionsGoal[Index]->DimmsNum, sizeof(DIMM *), CompareDimmOrderInInterleaveSet); } if (EFI_ERROR(Rc)) { goto Finish; } for (Index2 = 0; Index2 < pDimm->pRegionsGoal[Index]->DimmsNum; Index2++) { NVDIMM_IDENTIFICATION_INFORMATION *pIdentInfo = (NVDIMM_IDENTIFICATION_INFORMATION *)pCurrentOffset; if (IS_ACPI_HEADER_REV_MAJ_0_MIN_1((*ppConfigInput))) { pIdentInfo->DimmIdentification.Version1.DimmManufacturerId = pDimm->pRegionsGoal[Index]->pDimms[Index2]->Manufacturer; pIdentInfo->DimmIdentification.Version1.DimmSerialNumber = pDimm->pRegionsGoal[Index]->pDimms[Index2]->SerialNumber; CopyMem_S(pIdentInfo->DimmIdentification.Version1.DimmPartNumber, sizeof(pIdentInfo->DimmIdentification.Version1.DimmPartNumber), pDimm->pRegionsGoal[Index]->pDimms[Index2]->PartNumber, sizeof(pIdentInfo->DimmIdentification.Version1.DimmPartNumber)); } else { pIdentInfo->DimmIdentification.Version2.Uid.ManufacturerId = pDimm->pRegionsGoal[Index]->pDimms[Index2]->VendorId; if (pDimm->pRegionsGoal[Index]->pDimms[Index2]->ManufacturingInfoValid) { pIdentInfo->DimmIdentification.Version2.Uid.ManufacturingLocation = pDimm->pRegionsGoal[Index]->pDimms[Index2]->ManufacturingLocation; pIdentInfo->DimmIdentification.Version2.Uid.ManufacturingDate = pDimm->pRegionsGoal[Index]->pDimms[Index2]->ManufacturingDate; } pIdentInfo->DimmIdentification.Version2.Uid.SerialNumber = pDimm->pRegionsGoal[Index]->pDimms[Index2]->SerialNumber; } pIdentInfo->PmPartitionSize = PmPartitionSize; pIdentInfo->PartitionOffset = LastPersistentMemoryOffset; pCurrentOffset = (UINT8 *)pCurrentOffset + sizeof(NVDIMM_IDENTIFICATION_INFORMATION); } LastPersistentMemoryOffset += PmPartitionSize; } } else if (IS_ACPI_HEADER_REV_MAJ_1_OR_MAJ_3((*ppConfigInput))) { for (Index = 0; Index < pDimm->RegionsGoalNum; Index++) { NVDIMM_INTERLEAVE_INFORMATION3 *pInterleaveInfo = (NVDIMM_INTERLEAVE_INFORMATION3 *)pCurrentOffset; pInterleaveInfo->Header.Type = PCAT_TYPE_INTERLEAVE_INFORMATION_TABLE; pInterleaveInfo->Header.Length = (UINT16)sizeof(NVDIMM_INTERLEAVE_INFORMATION3) + (UINT16)pDimm->pRegionsGoal[Index]->DimmsNum * (UINT16)sizeof(NVDIMM_IDENTIFICATION_INFORMATION3); pInterleaveInfo->InterleaveSetIndex = pDimm->pRegionsGoal[Index]->InterleaveSetIndex; pInterleaveInfo->NumOfDimmsInInterleaveSet = (UINT8)pDimm->pRegionsGoal[Index]->DimmsNum; pInterleaveInfo->InterleaveMemoryType = NVDIMM_MEMORY_PERSISTENT_TYPE; pInterleaveInfo->InterleaveFormatChannel = pDimm->pRegionsGoal[Index]->ChannelInterleaving; pInterleaveInfo->InterleaveFormatImc = pDimm->pRegionsGoal[Index]->ImcInterleaving; pInterleaveInfo->InterleaveChangeStatus = 0; // Used by Config Output, 0 for Config Input pCurrentOffset = (UINT8 *)pCurrentOffset + sizeof(NVDIMM_INTERLEAVE_INFORMATION3); /** Identification Information tables **/ PmPartitionSize = pDimm->pRegionsGoal[Index]->Size / pDimm->pRegionsGoal[Index]->DimmsNum; /** Sort Dimms list according to BIOS requirement for Dimms order in interleave set **/ if (pDimm->pRegionsGoal[Index]->DimmsNum == NUM_OF_DIMMS_IN_SIX_WAY_INTERLEAVE_SET) { Rc = BubbleSort(pDimm->pRegionsGoal[Index]->pDimms, pDimm->pRegionsGoal[Index]->DimmsNum, sizeof(DIMM *), CompareDimmOrderInInterleaveSet6Way); } else { Rc = BubbleSort(pDimm->pRegionsGoal[Index]->pDimms, pDimm->pRegionsGoal[Index]->DimmsNum, sizeof(DIMM *), CompareDimmOrderInInterleaveSet); } if (EFI_ERROR(Rc)) { goto Finish; } for (Index2 = 0; Index2 < pDimm->pRegionsGoal[Index]->DimmsNum; Index2++) { NVDIMM_IDENTIFICATION_INFORMATION3 *pIdentInfo = (NVDIMM_IDENTIFICATION_INFORMATION3 *)pCurrentOffset; pIdentInfo->DimmIdentification.ManufacturerId = pDimm->pRegionsGoal[Index]->pDimms[Index2]->VendorId; if (pDimm->pRegionsGoal[Index]->pDimms[Index2]->ManufacturingInfoValid) { pIdentInfo->DimmIdentification.ManufacturingLocation = pDimm->pRegionsGoal[Index]->pDimms[Index2]->ManufacturingLocation; pIdentInfo->DimmIdentification.ManufacturingDate = pDimm->pRegionsGoal[Index]->pDimms[Index2]->ManufacturingDate; } pIdentInfo->DimmIdentification.SerialNumber = pDimm->pRegionsGoal[Index]->pDimms[Index2]->SerialNumber; pIdentInfo->PmPartitionSize = PmPartitionSize; pIdentInfo->PartitionOffset = LastPersistentMemoryOffset; pIdentInfo->DimmLocation.AsUint64 = 0; // Update the DIMM location field pPmttModuleInfo = GetDimmModuleByPidFromPmtt(pDimm->pRegionsGoal[Index]->pDimms[Index2]->DimmID, gNvmDimmData->PMEMDev.pPmttHead); if (pPmttModuleInfo != NULL) { pIdentInfo->DimmLocation.Split.SocketId = pPmttModuleInfo->SocketId; pIdentInfo->DimmLocation.Split.DieId = pPmttModuleInfo->DieId; pIdentInfo->DimmLocation.Split.MemControllerId = pPmttModuleInfo->MemControllerId; pIdentInfo->DimmLocation.Split.ChannelId = pPmttModuleInfo->ChannelId; pIdentInfo->DimmLocation.Split.SlotId = pPmttModuleInfo->SlotId; } else { NVDIMM_ERR("DIMM Module with pid: %d not found in PMTT", pDimm->pRegionsGoal[Index]->pDimms[Index2]->DimmID); pIdentInfo->DimmLocation.Split.SocketId = pDimm->pRegionsGoal[Index]->pDimms[Index2]->SocketId; pIdentInfo->DimmLocation.Split.DieId = MAX_DIEID_SINGLE_DIE_SOCKET; pIdentInfo->DimmLocation.Split.MemControllerId = pDimm->pRegionsGoal[Index]->pDimms[Index2]->ImcId; pIdentInfo->DimmLocation.Split.ChannelId = pDimm->pRegionsGoal[Index]->pDimms[Index2]->ChannelId; pIdentInfo->DimmLocation.Split.SlotId = pDimm->pRegionsGoal[Index]->pDimms[Index2]->ChannelPos; } pCurrentOffset = (UINT8 *)pCurrentOffset + sizeof(NVDIMM_IDENTIFICATION_INFORMATION3); } LastPersistentMemoryOffset += PmPartitionSize; } } if (pDimm->FwVer.FwApiMajor >= 3 && ReservedSizeIsZero) { // Match persistent memory partition size change request to the size of requested // persistent memory for this PMem module, instead of using the raw PMem module size pPartSizeChange->PmPartitionSize = LastPersistentMemoryOffset; } /** Extension table for Intel automatic provisioning **/ if (pIntelDIMMConfigEfiVar != NULL) { if (pIntelDIMMConfigEfiVar->ProvisionCapacityMode == PROVISION_CAPACITY_MODE_AUTO) { pAutoProvExtension = (CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE *) pCurrentOffset; pAutoProvExtension->Header.Type = PCAT_TYPE_CONFIG_MANAGEMENT_ATTRIBUTES_TABLE; pAutoProvExtension->Header.Length = sizeof(CONFIG_MANAGEMENT_ATTRIBUTES_EXTENSION_TABLE) + sizeof(INTEL_DIMM_CONFIG); pAutoProvExtension->VendorId = SPD_INTEL_VENDOR_ID; pAutoProvExtension->Guid = gIntelDimmConfigVariableGuid; pIntelDIMMConfigIn = (INTEL_DIMM_CONFIG *) pAutoProvExtension->pGuidData; pIntelDIMMConfigIn->Revision = INTEL_DIMM_CONFIG_REVISION; pIntelDIMMConfigIn->ProvisionCapacityMode = pIntelDIMMConfigEfiVar->ProvisionCapacityMode; pIntelDIMMConfigIn->MemorySize = pIntelDIMMConfigEfiVar->MemorySize; pIntelDIMMConfigIn->PMType = pIntelDIMMConfigEfiVar->PMType; pIntelDIMMConfigIn->ProvisionNamespaceMode = pIntelDIMMConfigEfiVar->ProvisionNamespaceMode; pIntelDIMMConfigIn->NamespaceFlags = pIntelDIMMConfigEfiVar->NamespaceFlags; pIntelDIMMConfigIn->NamespaceLabelVersion = pIntelDIMMConfigEfiVar->NamespaceLabelVersion; } } /** Generate checksum **/ GenerateChecksum(*ppConfigInput, (*ppConfigInput)->Header.Length, PCAT_TABLE_HEADER_CHECKSUM_OFFSET); Finish: FREE_POOL_SAFE(pIntelDIMMConfigEfiVar); FREE_POOL_SAFE(pConfHeader); NVDIMM_EXIT_I64(Rc); return Rc; } /** Generate checksum for pData. Entire table and checksum must sum to 0. Formula: entire table [without checksum field] % 256 [max byte value + 1] + checksum = 0 @param[in,out] pData Table that will generate the checksum for @param[in] Length Size of the pData @param[in] ChecksumOffset Offset that indicates the one-byte checksum field in the pData **/ VOID GenerateChecksum( IN OUT VOID *pData, IN UINT32 Length, IN UINT32 ChecksumOffset ) { UINT8 Checksum = 0; UINT8 *pByteData = (UINT8 *) pData; UINT32 Index = 0; if (pData == NULL) { NVDIMM_DBG("One or more parameters are NULL"); return; } pByteData[ChecksumOffset] = 0; for (Index = 0; Index < Length; Index++) { Checksum += pByteData[Index]; } pByteData[ChecksumOffset] = MAX_UINT8_VALUE - Checksum + 1; } /** Verify the checksum. Entire table, including its checksum field, must sum to 0. @param[in] pData Table that will validate the checksum for @param[in] Length Size of the pData @param[in] Checksum of pData @retval TRUE The table and the checksum sum to 0 @retval FALSE The table and the checksum not sum to 0 **/ BOOLEAN IsChecksumValid( IN VOID *pData, IN UINT32 Length, IN UINT8 Checksum ) { UINT8 Sum = 0; UINT8 *pByteData = (UINT8 *) pData; UINT32 Index = 0; if (pData == NULL) { NVDIMM_DBG("One or more parameters are NULL"); return FALSE; } for (Index = 0; Index < Length; Index++) { Sum += pByteData[Index]; } if (Sum != 0) { NVDIMM_DBG("Checksum(%d) missed by %d", Checksum, Sum); } return (Sum == 0) ? TRUE : FALSE; } /** Get new Sequence Number based on Platform Config Data Config Output table We read the last SequenceNumber from Config Output table and increase it by 1. If it will be max UINT32 (2^32 - 1) value, then after increasing we will get 0, what is fine. @param[in] pDimm Dimm that contains Config Output table @param[out] pSequenceNum Output variable for new Sequence Number **/ EFI_STATUS GetNewSequenceNumber( IN struct _DIMM *pDimm, OUT UINT32 *pSequenceNum ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_CONFIGURATION_HEADER *pPcdConfHeader = NULL; NVDIMM_PLATFORM_CONFIG_OUTPUT *pPcdConfOutput = NULL; NVDIMM_ENTRY(); if (pDimm == NULL || pSequenceNum == NULL) { NVDIMM_DBG("NULL pointer provided."); ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } ReturnCode = GetPlatformConfigDataOemPartition(pDimm, FALSE, &pPcdConfHeader); #ifdef MEMORY_CORRUPTION_WA if (ReturnCode == EFI_DEVICE_ERROR) { ReturnCode = GetPlatformConfigDataOemPartition(pDimm, FALSE, &pPcdConfHeader); } #endif // MEMORY_CORRUPTIO_WA if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("GetPlatformConfigDataOemPartition returned EFIReturnCode:%d", ReturnCode); goto Finish; } if (pPcdConfHeader->ConfOutputStartOffset == 0 || pPcdConfHeader->ConfOutputDataSize == 0) { NVDIMM_DBG("There is no Current Config table"); *pSequenceNum = 1; } else { pPcdConfOutput = GET_NVDIMM_PLATFORM_CONFIG_OUTPUT(pPcdConfHeader); if (!IsPcdConfOutputHeaderValid(pPcdConfOutput, pDimm->PcdOemPartitionSize )) { ReturnCode = EFI_ABORTED; goto Finish; } *pSequenceNum = pPcdConfOutput->SequenceNumber + 1; } Finish: FREE_POOL_SAFE(pPcdConfHeader); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Get Platform Config Data table by given type from current config @param[in] pCurrentConfig Current Config table @param[in] TableType table type to retrieve **/ VOID * GetPcatTableFromCurrentConfig( IN NVDIMM_CURRENT_CONFIG *pCurrentConfig, IN UINT8 TableType ) { VOID *pPartition = NULL; PCAT_TABLE_HEADER *pCurPcatTable = NULL; UINT32 SizeOfPcatTables = 0; if (pCurrentConfig == NULL || (TableType != PCAT_TYPE_PARTITION_SIZE_CHANGE_TABLE && TableType != PCAT_TYPE_INTERLEAVE_INFORMATION_TABLE)) { goto Finish; } pCurPcatTable = (PCAT_TABLE_HEADER *) &pCurrentConfig->pPcatTables; SizeOfPcatTables = pCurrentConfig->Header.Length - (UINT32) ((UINT8 *)pCurPcatTable - (UINT8 *)pCurrentConfig); while ((UINT32) ((UINT8 *) pCurPcatTable - (UINT8 *) &pCurrentConfig->pPcatTables) < SizeOfPcatTables) { if (pCurPcatTable->Type == TableType) { pPartition = pCurPcatTable; break; } pCurPcatTable = GET_VOID_PTR_OFFSET(pCurPcatTable, ((PCAT_TABLE_HEADER *) pCurPcatTable)->Length); } Finish: return pPartition; } /** Compare Dimm channel and iMC to determine Dimms order in interleave set This function supports 1-way, 2-way, 3-way and 4-way interleave sets @param[in] pFirst First item to compare @param[in] pSecond Second item to compare @retval -1 if first is less than second @retval 0 if first is equal to second @retval 1 if first is greater than second **/ INT32 CompareDimmOrderInInterleaveSet( IN VOID *pFirst, IN VOID *pSecond ) { DIMM *pDimmFirst = NULL; DIMM *pDimmSecond = NULL; if (pFirst == NULL || pSecond == NULL) { NVDIMM_DBG("NULL pointer found."); return 0; } pDimmFirst = *((DIMM **) pFirst); pDimmSecond = *((DIMM **) pSecond); if (pDimmFirst->ChannelId < pDimmSecond->ChannelId) { return -1; } else if (pDimmFirst->ChannelId > pDimmSecond->ChannelId) { return 1; } else { if (pDimmFirst->ImcId < pDimmSecond->ImcId) { return -1; } else if (pDimmFirst->ImcId > pDimmSecond->ImcId) { return 1; } else { return 0; } } } /** Compare Dimm channel and iMC to determine Dimms order in interleave set This function supports 6-way interleave set @param[in] pFirst First item to compare @param[in] pSecond Second item to compare @retval -1 if first is less than second @retval 0 if first is equal to second @retval 1 if first is greater than second **/ INT32 CompareDimmOrderInInterleaveSet6Way( IN VOID *pFirst, IN VOID *pSecond ) { DIMM *pDimmFirst = NULL; DIMM *pDimmSecond = NULL; UINT16 ParityResultFirst = 0; UINT16 ParityResultSecond = 0; if (pFirst == NULL || pSecond == NULL) { NVDIMM_DBG("NULL pointer found."); return 0; } pDimmFirst = *((DIMM **) pFirst); pDimmSecond = *((DIMM **) pSecond); /** 6-way order: [CH 0, iMC 0] [CH 1, iMC 1] [CH 2, iMC 0] [CH 0, iMC 1] [CH 1, iMC 0] [CH 2, iMC 1] **/ ParityResultFirst = (pDimmFirst->ChannelId + pDimmFirst->ImcId) % 2; ParityResultSecond = (pDimmSecond->ChannelId + pDimmSecond->ImcId) % 2; if (ParityResultFirst < ParityResultSecond) { return -1; } else if (ParityResultFirst > ParityResultSecond) { return 1; } else { if (pDimmFirst->ChannelId < pDimmSecond->ChannelId) { return -1; } else if (pDimmFirst->ChannelId > pDimmSecond->ChannelId) { return 1; } else { return 0; } } } /** Validate the PCD CIN header @param[in] pPcdConfInput Pointer to the PCD CIN Header @param[in] pSecond Max allowed size of the PCD OEM Partition @retval TRUE if valid @retval FALSE if invalid. **/ BOOLEAN IsPcdConfInputHeaderValid(NVDIMM_PLATFORM_CONFIG_INPUT *pPcdConfInput, UINT32 PcdOemPartitionSize) { if (NULL == pPcdConfInput) { NVDIMM_DBG("DIMM Config Input table is NULL"); } else if (pPcdConfInput->Header.Signature != NVDIMM_CONFIGURATION_INPUT_SIG) { NVDIMM_DBG("Incorrect signature of the DIMM Config Input table"); } else if (pPcdConfInput->Header.Length > PcdOemPartitionSize) { NVDIMM_DBG("Length of PCD Config Input header is greater than max PCD OEM partition size"); } else if (!IsChecksumValid(pPcdConfInput, pPcdConfInput->Header.Length, pPcdConfInput->Header.Checksum)) { NVDIMM_DBG("The checksum of Config Input table is invalid."); } else if (IS_ACPI_HEADER_REV_INVALID(pPcdConfInput)) { NVDIMM_DBG("Revision of PCD Config Input table is invalid"); } else { NVDIMM_DBG("The data in Config Input table is valid."); return TRUE; } return FALSE; } /** Validate the PCD COUT header @param[in] pPcdConfOutput Pointer to the PCD COUT Header @param[in] pSecond Max allowed size of the PCD OEM Partition @retval TRUE if valid @retval FALSE if invalid. **/ BOOLEAN IsPcdConfOutputHeaderValid(NVDIMM_PLATFORM_CONFIG_OUTPUT *pPcdConfOutput, UINT32 PcdOemPartitionSize) { if (NULL == pPcdConfOutput) { NVDIMM_DBG("DIMM Config Output table is NULL"); } else if (pPcdConfOutput->Header.Signature != NVDIMM_CONFIGURATION_OUTPUT_SIG) { NVDIMM_DBG("Incorrect signature of the DIMM Config Output table"); } else if (pPcdConfOutput->Header.Length > PcdOemPartitionSize) { NVDIMM_DBG("Length of PCD Config Output header is greater than max PCD OEM partition size"); } else if (!IsChecksumValid(pPcdConfOutput, pPcdConfOutput->Header.Length, pPcdConfOutput->Header.Checksum)) { NVDIMM_DBG("The checksum of Config Output table is invalid."); } else if (IS_ACPI_HEADER_REV_INVALID(pPcdConfOutput)) { NVDIMM_DBG("Revision of PCD Config Output table is invalid"); } else { NVDIMM_DBG("The data in Config Output table is valid."); return TRUE; } return FALSE; } /** Validate the PCD CCUR header @param[in] pPcdCurrentConf Pointer to the PCD CCUR Header @param[in] pSecond Max allowed size of the PCD OEM Partition @retval TRUE if valid @retval FALSE if invalid. **/ BOOLEAN IsPcdCurrentConfHeaderValid(NVDIMM_CURRENT_CONFIG *pPcdCurrentConf, UINT32 PcdOemPartitionSize) { if (NULL == pPcdCurrentConf) { NVDIMM_DBG("DIMM Current Config table is NULL"); } else if (pPcdCurrentConf->Header.Signature != NVDIMM_CURRENT_CONFIG_SIG) { NVDIMM_DBG("Incorrect signature of the DIMM Current Config table"); } else if (pPcdCurrentConf->Header.Length > PcdOemPartitionSize) { NVDIMM_DBG("Length of PCD Current Config header is greater than max PCD OEM partition size"); } else if (IS_ACPI_HEADER_REV_INVALID(pPcdCurrentConf)) { NVDIMM_DBG("Revision of PCD Current Config table is invalid"); } else if (!IsChecksumValid(pPcdCurrentConf, pPcdCurrentConf->Header.Length, pPcdCurrentConf->Header.Checksum)) { NVDIMM_DBG("The Current Config table checksum is invalid."); } else { NVDIMM_DBG("The data in Current Config table is valid."); return TRUE; } return FALSE; } ipmctl-03.00.00.0485/DcpmPkg/driver/Utils/PlatformConfigData.h000066400000000000000000000110521440615110200234070ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _PLATFORM_CONFIG_DATA_H_ #define _PLATFORM_CONFIG_DATA_H_ #include #include struct _DIMM; #if defined(_MSC_VER) #pragma warning( push ) #pragma warning( disable : 4200 ) #endif /** Generate PCD Configuration Input for the dimm based on its pools goal configuration The caller is responsible to free the allocated memory of PCD Config Input @param[in] pDimm the dimm that PCD Config Input is destined for @param[in] ReservedSizeIsZero Indicate whether the reserved size is zero @param[out] ppConfigInput new generated PCD Config Input @retval EFI_SUCCESS success @retval EFI_INVALID_PARAMETER one or more parameters are NULL @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS GeneratePcdConfInput( IN struct _DIMM *pDimm, IN BOOLEAN ReservedSizeIsZero, OUT NVDIMM_PLATFORM_CONFIG_INPUT **ppConfigInput ); /** Generate checksum for pData. Entire table and checksum must sum to 0. Formula: entire table [without checksum field] % 256 [max byte value + 1] + checksum = 0 @param[in,out] pData Table that will generate the checksum for @param[in] Length Size of the pData @param[in] ChecksumOffset Offset that indicates the one-byte checksum field in the pData **/ VOID GenerateChecksum( IN OUT VOID *pData, IN UINT32 Length, IN UINT32 ChecksumOffset ); /** Verify the checksum. Entire table, including its checksum field, must sum to 0. @param[in] pData Table that will validate the checksum for @param[in] Length Size of the pData @param[in] Checksum of pData @retval TRUE The table and the checksum sum to 0 @retval FALSE The table and the checksum not sum to 0 **/ BOOLEAN IsChecksumValid( IN VOID *pData, IN UINT32 Length, IN UINT8 Checksum ); /** Get new Sequence Number based on Platform Config Data Config Output table We read the last SequenceNumber from Config Output table and increase it by 1. If it will be max UINT32 (2^32 - 1) value, then after increasing we will get 0, what is fine. @param[in] pDimm Dimm that contains Config Output table @param[out] pSequenceNum Output variable for new Sequence Number **/ EFI_STATUS GetNewSequenceNumber( IN struct _DIMM *pDimm, OUT UINT32 *pSequenceNum ); /** Get Platform Config Data table by given type from current config @param[in] pCurrentConfig Current Config table @param[in] TableType table type to retrieve **/ VOID * GetPcatTableFromCurrentConfig( IN NVDIMM_CURRENT_CONFIG *pCurrentConfig, IN UINT8 TableType ); /** Compare Dimm channel and iMC to determine Dimms order in interleave set This function supports 1-way, 2-way, 3-way and 4-way interleave sets @param[in] pFirst First item to compare @param[in] pSecond Second item to compare @retval -1 if first is less than second @retval 0 if first is equal to second @retval 1 if first is greater than second **/ INT32 CompareDimmOrderInInterleaveSet( IN VOID *pFirst, IN VOID *pSecond ); /** Compare Dimm channel and iMC to determine Dimms order in interleave set This function supports 6-way interleave set @param[in] pFirst First item to compare @param[in] pSecond Second item to compare @retval -1 if first is less than second @retval 0 if first is equal to second @retval 1 if first is greater than second **/ INT32 CompareDimmOrderInInterleaveSet6Way( IN VOID *pFirst, IN VOID *pSecond ); /** Validate the PCD CIN header @param[in] pPcdConfInput Pointer to the PCD CIN Header @param[in] pSecond Max allowed size of the PCD OEM Partition @retval TRUE if valid @retval FALSE if invalid. **/ BOOLEAN IsPcdConfInputHeaderValid( IN NVDIMM_PLATFORM_CONFIG_INPUT *pPcdConfInput, IN UINT32 PcdOemPartitionSize ); /** Validate the PCD COUT header @param[in] pPcdConfOutput Pointer to the PCD COUT Header @param[in] pSecond Max allowed size of the PCD OEM Partition @retval TRUE if valid @retval FALSE if invalid. **/ BOOLEAN IsPcdConfOutputHeaderValid( IN NVDIMM_PLATFORM_CONFIG_OUTPUT *pPcdConfOutput, IN UINT32 PcdOemPartitionSize ); /** Validate the PCD CCUR header @param[in] pPcdCurrentConf Pointer to the PCD CCUR Header @param[in] pSecond Max allowed size of the PCD OEM Partition @retval TRUE if valid @retval FALSE if invalid. **/ BOOLEAN IsPcdCurrentConfHeaderValid( IN NVDIMM_CURRENT_CONFIG *pPcdCurrentConf, IN UINT32 PcdOemPartitionSize ); #if defined(_MSC_VER) #pragma warning( pop ) #endif #endif ipmctl-03.00.00.0485/DcpmPkg/driver/Utils/ProcessorAndTopologyInfo.c000066400000000000000000000077041440615110200246620ustar00rootroot00000000000000#include "ProcessorAndTopologyInfo.h" #include #ifdef OS_BUILD #include #endif // 2 memory controllers, 3 channels // where bit placement represents the // PMem modules ordered as such: // --------- // CH0 CH1 CH2 // IMC0 | 0b000001 | 0b000010 | 0b000100 | // IMC1 | 0b001000 | 0b010000 | 0b100000 | // --------- UINT32 INTERLEAVE_SETS_2_3[] = { 0x3F, //0b111111 x6 0x1B, //0b011011 x4 0x2D, //0b101101 x4 0x36, //0b110110 x4 0x07, //0b000111 x3 0x38, //0b111000 x3 // favor across memory controller 0x09, //0b001001 x2 0x12, //0b010010 x2 0x24, //0b100100 x2 // before across channel 0x03, //0b000011 x2 0x05, //0b000101 x2 0x06, //0b000110 x2 0x18, //0b011000 x2 0x28, //0b101000 x2 0x30, //0b110000 x2 }; /** Get the topology and InterleaveSetMap Info based on the processor type @param[out] piMCNum Number of iMCs per CPU. @param[out] pChannelNum Number of channels per iMC @param[out] ppInterleaveMap Pointer to InterleaveSetMap based on the processor type @retval EFI_SUCCESS Ok @retval EFI_INVALID_PARAMETER invalid parameter @retval EFI_OUT_OF_RESOURCES memory allocation failed **/ EFI_STATUS GetTopologyAndInterleaveSetMapInfo( OUT UINT8 *piMCNum OPTIONAL, OUT UINT8 *pChannelNum OPTIONAL, OUT UINT32 **ppInterleaveMap OPTIONAL ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT32 NumOfiMCsPerCPU = 0; UINT32 NumOfChannelsPeriMC = 0; UINT32 InterleaveMapListLength = 0; UINT32 Index = 0; NVDIMM_ENTRY(); // If topology cannot be determined, then we return an error CHECK_RESULT(RetrievePlatformTopologyFromPmtt(&NumOfiMCsPerCPU, &NumOfChannelsPeriMC), Finish); if (ppInterleaveMap != NULL) { ReturnCode = RetrieveInterleaveSetMap(ppInterleaveMap, &InterleaveMapListLength); /** BIOS did not publish the interleave format list in PCAT or BIOS published it but the topology cannot be determined, so interleave bitmap cannot be interpreted correctly. In both cases, fallback to the default list for 2 iMcsPerCPU & 3 ChannelsPeriMC topology. **/ if (ReturnCode == EFI_NOT_FOUND) { FREE_POOL_SAFE(*ppInterleaveMap); *ppInterleaveMap = AllocateZeroPool(sizeof(INTERLEAVE_SETS_2_3)); if (*ppInterleaveMap == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_WARN("Memory allocation error."); goto Finish; } CopyMem_S(*ppInterleaveMap, sizeof(INTERLEAVE_SETS_2_3), INTERLEAVE_SETS_2_3, sizeof(INTERLEAVE_SETS_2_3)); InterleaveMapListLength = sizeof(INTERLEAVE_SETS_2_3)/sizeof(INTERLEAVE_SETS_2_3[0]); ReturnCode = EFI_SUCCESS; } else if (EFI_ERROR(ReturnCode)) { NVDIMM_DBG("RetrieveInterleaveSetMap failed."); goto Finish; } // Append the x1 bitmaps to the interleave format list received from BIOS *ppInterleaveMap = ReallocatePool(sizeof(**ppInterleaveMap) * InterleaveMapListLength, sizeof(**ppInterleaveMap) * (InterleaveMapListLength + (NumOfiMCsPerCPU * NumOfChannelsPeriMC) + 1), *ppInterleaveMap); if (*ppInterleaveMap == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_WARN("Memory allocation error."); goto Finish; } for (Index = 0; Index < (NumOfiMCsPerCPU * NumOfChannelsPeriMC); Index++) { (*ppInterleaveMap)[Index + InterleaveMapListLength] = INTERLEAVE_BY_ONE_BITMAP_IMC0_CH0 << Index; } (*ppInterleaveMap)[Index + InterleaveMapListLength] = END_OF_INTERLEAVE_SETS; } if (piMCNum != NULL) { *piMCNum = NumOfiMCsPerCPU & MAX_UINT8; } if (pChannelNum != NULL) { *pChannelNum = NumOfChannelsPeriMC & MAX_UINT8; } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/driver/Utils/ProcessorAndTopologyInfo.h000066400000000000000000000016741440615110200246670ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _PROCESSOR_AND_TOPOLOGY_INFO_H_ #define _PROCESSOR_AND_TOPOLOGY_INFO_H_ #include #include #define END_OF_INTERLEAVE_SETS 0 #define DIMMS_PER_CHANNEL 2 #define INTERLEAVE_BY_ONE_BITMAP_IMC0_CH0 1 /** Get the topology and InterleaveSetMap Info based on the processor type @param[out] piMCNum Number of iMCs per CPU. @param[out] pChannelNum Number of channels per iMC @param[out] ppInterleaveMap Pointer to InterleaveSetMap based on the processor type @retval EFI_SUCCESS Ok @retval EFI_INVALID_PARAMETER invalid parameter @retval EFI_OUT_OF_RESOURCES memory allocation failed **/ EFI_STATUS GetTopologyAndInterleaveSetMapInfo( OUT UINT8 *piMCNum OPTIONAL, OUT UINT8 *pChannelNum OPTIONAL, OUT UINT32 **ppInterleaveMap OPTIONAL ); #endif /* _PROCESSOR_AND_TOPOLOGY_INFO_H_ */ ipmctl-03.00.00.0485/DcpmPkg/driver/Utils/SmbiosUtility.c000066400000000000000000000176111440615110200225250ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include "SmbiosUtility.h" #include #include #include #include #include /** Retrieve Capacity for the given SMBIOS version. @param[in] Size Size field @param[in] ExtendedSize Extended size field @param[in] SmbiosVersion The SMBIOS version @param[out] pCapacity Pointer to the Capacity @retval EFI_SUCCESS Retrieval was successful @retval EFI_INVALID_PARAMETER Null parameter passed **/ EFI_STATUS GetSmbiosCapacity ( IN UINT16 Size, IN UINT32 ExtendedSize, IN SMBIOS_VERSION SmbiosVersion, OUT UINT64 *pCapacity ) { EFI_STATUS ReturnCode = EFI_SUCCESS; UINT64 Capacity = 0; NVDIMM_ENTRY(); if (pCapacity == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (SmbiosVersion.Major >= 2) { if (Size == MAX_UINT16) { *pCapacity = 0; goto Finish; } if (SmbiosVersion.Minor >= 7 || SmbiosVersion.Major == 3) { if (Size == MAX_INT16) { *pCapacity = MIB_TO_BYTES(ExtendedSize); goto Finish; } } Capacity = Size & MAX_UINT16; if (Size & BIT15) { *pCapacity = KIB_TO_BYTES(Capacity); } else { *pCapacity = MIB_TO_BYTES(Capacity); } } ReturnCode = EFI_SUCCESS; Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Retrieve SMBIOS string for the given string number. It is the caller's responsibility to use FreePool() to free the allocated buffer (ppSmbiosString). @param[in] pSmbios Pointer to SMBIOS structure. @param[in] StringNumber String number to return. @param[out] pSmbiosString Pointer to a char buffer to where SMBIOS string will be copied. @param[in] BufferStrLen pSmbiosString buffer string length (number of characters) @retval EFI_SUCCESS String retrieved successfully @retval EFI_INVALID_PARAMETER @retval EFI_NOT_FOUND **/ EFI_STATUS GetSmbiosString ( IN SMBIOS_STRUCTURE_POINTER *pSmbios, IN UINT16 StringNumber, OUT CHAR16 *pSmbiosString, IN UINT16 BufferStrLen ) { UINT16 Index; CHAR8 *pString = NULL; EFI_STATUS ReturnCode = EFI_NOT_FOUND; NVDIMM_ENTRY(); if (!pSmbios || !pSmbiosString) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } if (BufferStrLen == 0 && StringNumber != SMBIOS_STRING_INVALID) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /** Skip over formatted section **/ pString = (CHAR8 *) (pSmbios->Raw + pSmbios->Hdr->Length); /** Look through unformatted section **/ for (Index = 1; Index <= StringNumber; Index++) { if (StringNumber == Index) { if (AsciiStrLen(pString) > (BufferStrLen * sizeof(CHAR16))) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } AsciiStrToUnicodeStrS(pString, pSmbiosString, BufferStrLen); ReturnCode = EFI_SUCCESS; goto Finish; } /** Skip string **/ for (; *pString != 0; pString++); pString++; if (*pString == 0) { // If double NULL then we are done. String with given number not found. ReturnCode = EFI_NOT_FOUND; goto Finish; } } Finish: NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } /** Fill SmBios structures for first and last entry @param[out] pSmBiosStruct - pointer for first SmBios entry @param[out] pLastSmBiosStruct - pointer for last SmBios entry **/ #ifndef OS_BUILD // use the one defined in os_efi_api_io.c EFI_STATUS GetFirstAndBoundSmBiosStructPointer( OUT SMBIOS_STRUCTURE_POINTER *pSmBiosStruct, OUT SMBIOS_STRUCTURE_POINTER *pLastSmBiosStruct, OUT SMBIOS_VERSION *pSmbiosVersion ) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; SMBIOS_TABLE_ENTRY_POINT *pTableEntry = NULL; SMBIOS_TABLE_ENTRY_POINT *pTempTableEntry = NULL; SMBIOS_TABLE_ENTRY_POINT_3 *pTableEntry3 = NULL; UINT32 Index = 0; UINT8 MinorVersion = 0; PbrContext *pContext = PBR_CTX(); PbrSmbiosTableRecord *pSmbiosRecord = NULL; UINT32 TableSize = 0; CHECK_NULL_ARG(pSmBiosStruct, Finish); CHECK_NULL_ARG(pLastSmBiosStruct, Finish); CHECK_NULL_ARG(pSmbiosVersion, Finish); if (PBR_PLAYBACK_MODE != PBR_GET_MODE(pContext)) { /** get the smbios tables from the system configuration table **/ for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { if (CompareGuid(&gEfiSmbios3TableGuid, &(gST->ConfigurationTable[Index].VendorGuid))) { // Priority given to SMBIOS 3 - break out pTableEntry3 = (SMBIOS_TABLE_ENTRY_POINT_3 *)gST->ConfigurationTable[Index].VendorTable; break; } if (CompareGuid(&gEfiSmbiosTableGuid, &(gST->ConfigurationTable[Index].VendorGuid))) { // Even if we find this GUID, let's keep searching in case we find SMBIOS 3 pTempTableEntry = (SMBIOS_TABLE_ENTRY_POINT *)gST->ConfigurationTable[Index].VendorTable; if (MinorVersion < pTempTableEntry->MinorVersion) { // Preference given to a higher 2.x version pTableEntry = pTempTableEntry; MinorVersion = pTableEntry->MinorVersion; } } } if (pTableEntry3 != NULL) { pSmBiosStruct->Raw = (UINT8 *)(UINTN)(pTableEntry3->TableAddress); pLastSmBiosStruct->Raw = pSmBiosStruct->Raw + pTableEntry3->TableMaxSize; pSmbiosVersion->Major = pTableEntry3->MajorVersion; pSmbiosVersion->Minor = pTableEntry3->MinorVersion; TableSize = pTableEntry3->TableMaxSize; } else if (pTableEntry != NULL) { pSmBiosStruct->Raw = (UINT8 *)(UINTN)(pTableEntry->TableAddress); pLastSmBiosStruct->Raw = pSmBiosStruct->Raw + pTableEntry->TableLength; pSmbiosVersion->Major = pTableEntry->MajorVersion; pSmbiosVersion->Minor = pTableEntry->MinorVersion; TableSize = pTableEntry->TableLength; } CHECK_RESULT_MALLOC(pSmbiosRecord, (PbrSmbiosTableRecord *)AllocateZeroPool(sizeof(PbrSmbiosTableRecord) + TableSize), Finish); pSmbiosRecord->Size = TableSize; pSmbiosRecord->Minor = pSmbiosVersion->Minor; pSmbiosRecord->Major = pSmbiosVersion->Major; pSmbiosRecord->Minor = pSmbiosVersion->Minor; CopyMem_S(pSmbiosRecord->Table, TableSize, pSmBiosStruct->Raw, TableSize); CHECK_RESULT(PbrSetTableRecord(pContext, PBR_RECORD_TYPE_SMBIOS, pSmbiosRecord, sizeof(PbrSmbiosTableRecord) + TableSize), Finish); } else { // Playback mode CHECK_RESULT(PbrGetTableRecord(pContext, PBR_RECORD_TYPE_SMBIOS, (VOID**)&pSmbiosRecord, &TableSize), Finish); NVDIMM_DBG("Successfully retrieved SMBIOS record, Max smbios size %x\n", TableSize); pSmBiosStruct->Raw = pSmbiosRecord->Table; pLastSmBiosStruct->Raw = pSmBiosStruct->Raw + pSmbiosRecord->Size; pSmbiosVersion->Major = pSmbiosRecord->Major; pSmbiosVersion->Minor = pSmbiosRecord->Minor; } ReturnCode = EFI_SUCCESS; Finish: FREE_POOL_SAFE(pSmbiosRecord); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } #endif /** Move current pTable pointer to the next SMBIOS structure. @param[in,out] pSmbios Pointer to SMBIOS structure. @retval EFI_SUCCESS Structure successfully updated @retval EFI_INVALID_PARAMETER @retval EFI_NOT_FOUND **/ EFI_STATUS GetNextSmbiosStruct( IN OUT SMBIOS_STRUCTURE_POINTER *pTable ) { UINT8 Index = 0; CHAR8 *pSmbiosStr = NULL; EFI_STATUS ReturnCode = EFI_NOT_FOUND; if (pTable == NULL || pTable->Hdr == NULL) { ReturnCode = EFI_INVALID_PARAMETER; goto Finish; } /** Skip over formatted section **/ pSmbiosStr = (CHAR8 *) (pTable->Raw + pTable->Hdr->Length); for (Index = 0; Index < MAX_UINT8; Index++) { /** Skip string **/ for (; *pSmbiosStr != '\0'; pSmbiosStr++); pSmbiosStr++; if (*pSmbiosStr == 0) { pTable->Raw = (UINT8 *) ++pSmbiosStr; ReturnCode = EFI_SUCCESS; goto Finish; } } Finish: return ReturnCode; } ipmctl-03.00.00.0485/DcpmPkg/driver/Utils/SmbiosUtility.h000066400000000000000000000051711440615110200225300ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _SMBIOS_UTILITY_H_ #define _SMBIOS_UTILITY_H_ #include #include #define SMBIOS_STRING_INVALID 0 typedef struct { UINT8 AnchorString[5]; UINT8 EntryPointStructureChecksum; UINT8 EntryPointLength; UINT8 MajorVersion; UINT8 MinorVersion; UINT8 DocRev; UINT8 EntryPointRevision; UINT8 Reserved; UINT32 TableMaxSize; UINT64 TableAddress; } SMBIOS_TABLE_ENTRY_POINT_3; typedef struct { UINT8 Major; UINT8 Minor; } SMBIOS_VERSION; /** Retrieve Capacity for the given SMBIOS version. @param[in] Size Size field @param[in] ExtendedSize Extended size field @param[in] SmbiosVersion The SMBIOS version @param[out] pCapacity Pointer to the Capacity @retval EFI_SUCCESS Retrieval was successful @retval EFI_INVALID_PARAMETER Null parameter passed **/ EFI_STATUS GetSmbiosCapacity ( IN UINT16 Size, IN UINT32 ExtendedSize, IN SMBIOS_VERSION SmbiosVersion, OUT UINT64 *pCapacity ); /** Retrieve SMBIOS string for the given string number. It is the caller's responsibility to use FreePool() to free the allocated buffer (ppSmbiosString). @param[in] pSmbios Pointer to SMBIOS structure. @param[in] StringNumber String number to return. @param[out] pSmbiosString Pointer to a char buffer to where SMBIOS string will be copied. Buffer must be allocated. @param[in] BufferLen pSmbiosString buffer length @retval EFI_SUCCESS String retrieved successfully @retval EFI_INVALID_PARAMETER @retval EFI_NOT_FOUND **/ EFI_STATUS GetSmbiosString ( IN SMBIOS_STRUCTURE_POINTER *pSmbios, IN UINT16 StringNumber, OUT CHAR16 *ppSmbiosString, IN UINT16 BufferLen ); /** Move current pTable pointer to the next SMBIOS structure. @param[in,out] pSmbios Pointer to SMBIOS structure. @retval EFI_SUCCESS Structure successfully updated @retval EFI_INVALID_PARAMETER @retval EFI_NOT_FOUND **/ EFI_STATUS GetNextSmbiosStruct ( IN OUT SMBIOS_STRUCTURE_POINTER *pTable ); /** Fill SmBios structures for first and bound entry @param[out] pSmBiosStruct - pointer for first SmBios entry @param[out] pBoundSmBiosStruct - pointer for nonexistent (one after last) SmBios entry **/ EFI_STATUS GetFirstAndBoundSmBiosStructPointer( OUT SMBIOS_STRUCTURE_POINTER *pSmBiosStruct, OUT SMBIOS_STRUCTURE_POINTER *pLastSmBiosStruct, OUT SMBIOS_VERSION *pSmbiosVersion ); #endif /* _SMBIOS_UTILITY_H_ */ ipmctl-03.00.00.0485/Documentation/000077500000000000000000000000001440615110200164245ustar00rootroot00000000000000ipmctl-03.00.00.0485/Documentation/ipmctl/000077500000000000000000000000001440615110200177145ustar00rootroot00000000000000ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/000077500000000000000000000000001440615110200207425ustar00rootroot00000000000000ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-delete-pcd.txt000066400000000000000000000107521440615110200250040ustar00rootroot00000000000000// Copyright (c) 2021, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-delete-pcd(1) ==================== endif::manpage[] NAME ---- ipmctl-delete-pcd - Clears select partition data from the PCD SYNOPSIS -------- [listing] -- ipmctl delete [OPTIONS] -pcd [TARGETS] -- DESCRIPTION ----------- ifndef::os_build[] When LSA is specified, the namespace label storage area partition in the platform configuration data from one or more PMem modules are cleared. This is a destructive operation which will clear the entire namespace label storage area including all namespaces labels and the namespace label index block in order to re-purpose the PMem modules for use in a different operating system. All data on any deleted namespaces becomes inaccessible. NOTE: Deleting PCD LSA partition data removes any logical OS namespace mapping to the persistent memory data, but does not explicitly delete or modify user data found in persistent memory. endif::os_build[] When Config is specified, the Current, Input, and Output Data Size and Start Offset values in the Configuration header are set to zero, making those tables invalid. NOTE: When Config is specified, only PCD partition 1 is modified. If the platform is rebooted prior to creating a new goal on any targeted PMem modules, UEFI platform firmware will detect the missing tables and, if possible, restore previous config using the PCD partition 0 tables. NOTE: This action can be useful when moving PMem modules from one system to another, as goal creation rules may restrict provisioning PMem modules with an existing configuration. Deleting the PCD can be used as a way to prepare a PMem module for provisioning with the create -goal or load -goal commands by clearing existing configuration metadata. This allows the PMem module to be provisioned in isolation using the create-goal command with the -dimm option. Once the PCD has been deleted the desired goal should be created before rebooting. WARNING: *This command may result in data loss. Data should be backed up to other storage before executing this command. Because of data dependencies, other commands may be affected until the system has been rebooted*. OPTIONS ------- -f:: -force:: Deleting the PCD data is a destructive operation which requires confirmation from the user for each PMem module. This option suppresses the confirmation. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -lpmb:: Used to specify large transport payload size for the current invocation of ipmctl. -spmb:: Used to specify small transport payload size for the current invocation of ipmctl. NOTE: The -lpmb and -spmb options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] TARGETS ------- -dimm [DimmIDs]:: Deletes the PCD data on specific PMem modules by supplying one or more comma separated PMem module identifiers. The default is to delete the PCD data for all manageable PMem modules. ifndef::os_build[] -pcd [Config|LSA]:: Restricts clearing select partition data in the platform configuration data area. The default is to clear both. One of: * Config - Configuration management information * LSA - Namespace label storage area endif::os_build[] ifdef::os_build[] -pcd [Config]:: Clears the configuration management information. endif::os_build[] EXAMPLES -------- ifndef::os_build[] Clears the namespace label storage area from all manageable PMem modules [listing] -- delete -dimm -pcd LSA -- endif::os_build[] Clears the Cin, Cout, Ccur tables from all manageable PMem modules [listing] -- delete -dimm -pcd Config -- LIMITATIONS ----------- The specified PMem modules must be manageable by the host software, and if data-at-rest security is enabled, the PMem modules must be unlocked. Any existing namespaces associated with the requested PMem modules should be deleted before running this command. RETURN DATA ----------- For each PMem module, the CLI will indicate the status of the operation. If a failure occurs when deleting the platform configuration data from multiple PMem modules, the process will continue deleting the remaining PMem modules. ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-diagnostic-events.txt000066400000000000000000000375371440615110200264360ustar00rootroot00000000000000// Copyright (C) 2019, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause Events are generated as a result of invoking the Start Diagnostics command in order to analyze the Intel(R) Optane(TM) PMem module for potential issues. Diagnostic events may fall into the following categories: * Quick health diagnostic test event * Platform configuration diagnostic test event * Security diagnostic test event * Firmware consistency and settings diagnostic test event Each event includes the following pieces of information: * The severity of the event that occurred. One of: ** Informational (Info) ** Warning (Warning) ** Error (Failed) ** Aborted (Aborted) * A unique ID of the item (PMem module UUID, DimmID, NamespaceID, RegionID, etc.) the event refers to. * A detailed description of the event in English. The following sections list each of the possible events grouped by category of the event. == Quick Health Check Events The quick health check diagnostic verifies that the Intel(R) Optane(TM) PMem module's host mailboxes are accessible and that basic health indicators can be read and are currently reporting acceptable values. .Table Quick Health Check Events //// asciidoctor-pdf only renders as expected with .autowidth.stretch is used asciidoc does not recognize .autowidth and reports an error //// ifndef::backend-pdf[] [options="autowidth"] [role="stretch"] [cols="8,12,50,30"] endif::backend-pdf[] ifdef::backend-pdf[] [.autowidth.stretch] [cols="8,12,50,30"] endif::backend-pdf[] |=== |Code |Severity |Message |Arguments |500 |Info |The quick health check succeeded. | |501 |Warning |The quick health check detected that PMem module [1] is not manageable because subsystem vendor ID [2] is not supported. UID: [3] a|[.text-left] . PMem module Handle . Subsystem Vendor ID . PMem module UID |502 |Warning |The quick health check detected that PMem module [1] is not manageable because subsystem device ID [2] is not supported. UID: [3] a|[.text-left] . PMem module Handle . Subsystem Device ID . PMem module UID |503 |Warning |The quick health check detected that PMem module [1] is not manageable because firmware API version [2] is not supported. UID: [3] a|[.text-left] . PMem module Handle . FW API version . PMem module UID |504 |Warning |The quick health check detected that PMem module [1] is reporting a bad health state [2]. UID: [3] a|[.text-left] . PMem module Handle . Actual Health State . PMem module UID |505 |Warning |The quick health check detected that PMem module [1] is reporting a media temperature of [2] C which is above the alarm threshold [3] C. UID: [4] a|[.text-left] . PMem module Handle . Actual Media Temperature . Media Temperature Threshold . PMem module UID |506 |Warning |The quick health check detected that PMem module [1] is reporting percentage remaining at [2]% which is less than the alarm threshold [3]%. UID: [4] a|[.text-left] . PMem module Handle . Actual Percentage Remaining . Percentage Remaining Threshold . PMem module UID |507 |Warning |The quick health check detected that PMem module [1] is reporting reboot required. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |511 |Warning |The quick health check detected that PMem module [1] is reporting a controller temperature of [2] C which is above the alarm threshold [3] C. UID: [4] a|[.text-left] . PMem module Handle . Actual Controller Temperature . Controller Temperature Threshold . PMem module UID |513 |Error |The quick health check detected that the boot status register of PMem module [1] is not readable. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |514 |Error |The quick health check detected that the firmware on PMem module [1] is reporting that the media is not ready. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |515 |Error |The quick health check detected that the firmware on PMem module [1] is reporting an error in the media. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |519 |Error |The quick health check detected that PMem module [1] failed to initialize BIOS POST testing. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |520 |Error |The quick health check detected that the firmware on PMem module [1] has not initialized successfully. The last known Major:Minor Checkpoint is [2]. UID: [3] a|[.text-left] . PMem module Handle . Major checkpoint : Minor checkpoint in Boot Status Register . PMem module UID |523 |Error |The quick health check detected that PMem module [1] is reporting a viral state. The PMem module is now read-only. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |529 |Warning |The quick health check detected that PMem module [1] is reporting that it has no package spares available. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |530 |Info |The quick health check detected that the firmware on PMem module [1] experienced an unsafe shutdown before its latest restart. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |533 |Error |The quick health check detected that the firmware on PMem module [1] is reporting that the AIT DRAM is not ready. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |534 |Error |The quick health check detected that the firmware on PMem module [1] is reporting that the media is disabled. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |535 |Error |The quick health check detected that the firmware on PMem module [1] is reporting that the AIT DRAM is disabled. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |536 |Error |The quick health check detected that the firmware on PMem module [1] failed to load successfully. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |538 |Error |PMem module [1] is reporting that the DDRT IO Init is not complete. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |539 |Error |PMem module [1] is reporting that the mailbox interface is not ready. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |540 |Error |An internal error caused the quick health check to abort on PMem module [1]. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |541 |Error |The quick health check detected that PMem module [1] is busy. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |542 |Error |The quick health check detected that the platform FW did not map a region to SPA on PMem module [1]. ACPI NFIT NVPMem module State Flags Error Bit 6 Set. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |543 |Error |The quick health check detected that PMem module [1] DDRT Training is not complete/failed. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |544 |Error |PMem module [1] is reporting that the DDRT IO Init is not started. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |545 |Error |The quick health check detected that the ROM on PMem module [1] has failed to complete initialization, last known Major:Minor Checkpoint is [2]. a| . PMem module Handle . Major checkpoint : Minor checkpoint in Boot Status Register . PMem module UID |=== == Platform Configuration Check Events This diagnostic test group verifies that the BIOS platform configuration matches the installed hardware and the platform configuration conforms to best known practices. .Table Platform Configuration Check Events //// asciidoctor-pdf only renders as expected with .autowidth.stretch is used asciidoc does not recognize .autowidth and reports an error //// ifndef::backend-pdf[] [options="autowidth"] [role="stretch"] [cols="8,12,50,30"] endif::backend-pdf[] ifdef::backend-pdf[] [.autowidth.stretch] [cols="8,12,50,30"] endif::backend-pdf[] |=== |Code |Severity |Message |Arguments |600 |Info |The platform configuration check succeeded. | |601 |Info |The platform configuration check detected that there are no manageable PMem modules. | |606 |Info |The platform configuration check detected that PMem module [1] is not configured. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |608 |Error |The platform configuration check detected [1] PMem modules installed on the platform with the same serial number [2]. a|[.text-left] . Number of PMem modules with duplicate serial numbers. . The duplicate serial number |609 |Info |The platform configuration check detected that PMem module [1] has a goal configuration that has not yet been applied. A system reboot is required for the new configuration to take effect. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |618 |Error |The platform configuration check detected that a PMem module with physical ID [1] is present in the system but failed to initialize. UID: [2] a|[.text-left] . PMem module handle in the SMBIOS table . PMem module UID |621 |Error |The platform configuration check detected PCD contains invalid data on PMem module [1]. UID: [2] a|[.text-left] . PMem module Handle . PMem module UID |622 |Error |The platform configuration check was unable to retrieve the namespace information. | |623 |Warning |The platform configuration check detected that the BIOS settings do not currently allow memory provisioning from this software. | |624 |Error |The platform configuration check detected that the BIOS could not apply the configuration goal on PMem module [1] because of errors in the goal data. The detailed status is COUT table status: [2] [3], Partition change table status: [4], Interleave change table 1 status: [5], Interleave change table 2 status: [6]. a|[.text-left] . PMem module Handle . Validation Status . Text error code corresponding to the status code . Partition Size Change Status . Interleave Change Status . Interleave Change Status |625 |Error |The platform configuration check detected that the BIOS could not apply the configuration goal on PMem module [1] because the system has insufficient resources. The detailed status is COUT table status: [2] [3], Partition change table status: [4], Interleave change table 1 status: [5], Interleave change table 2 status: [6]. a|[.text-left] . PMem module Handle . Validation Status . Text error code corresponding to the status code . Partition Size Change Status . Interleave Change Status . Interleave Change Status |626 |Error |The platform configuration check detected that the BIOS could not apply the configuration goal on PMem module [1] because of a firmware error. The detailed status is COUT table status: [2] [3], Partition change table status: [4], Interleave change table 1 status: [5], Interleave change table 2 status: [6]. a|[.text-left] . PMem module Handle . Validation Status . Text error code corresponding to the status code . Partition Size Change Status . Interleave Change Status . Interleave Change Status |627 |Error |The platform configuration check detected that the BIOS could not apply the configuration goal on PMem module [1] for an unknown reason. The detailed status is COUT table status: [2] [3], Partition change table status: [4], Interleave change table 1 status: [5], Interleave change table 2 status: [6]. a|[.text-left] . PMem module Handle . Validation Status . Text error code corresponding to the status code . Partition Size Change Status . Interleave Change Status . Interleave Change Status |628 |Error |The platform configuration check detected that interleave set [1] is broken because the PMem modules were moved [2]. a|[.text-left] . Interleave set index ID . List of moved PMem modules. |629 |Error |The platform configuration check detected that the platform does not support ADR and therefore data integrity is not guaranteed on the PMem modules. | |630 |Error |An internal error caused the platform configuration check to abort. | |631 |Error |The platform configuration check detected that interleave set [1] is broken because the PMem module with UID: [2] is missing from location (Socket-Die-iMC-Channel-Slot) [3]. a|[.text-left] . Interleave set index ID . PMem module UID . Location ID |632 |Error |The platform configuration check detected that interleave set [1] is broken because the PMem module with UID: [2] is misplaced. It is currently in location (Socket-Die-iMC-Channel-Slot) [3] and should be moved to (Socket-Die-iMC-Channel-Slot) [4]. a|[.text-left] . Interleave set index ID . PMem module UID . Location ID . Location ID |633 |Error |The platform configuration check detected that the BIOS could not fully map memory on PMem module [1] because of an error in current configuration. The detailed status is CCUR table status: [2] [3]. a|[.text-left] . PMem module Handle . Current Configuration Status . Text error code corresponding to the status code |=== == Security Check Events The security check diagnostic test group verifies that all Intel(R) Optane(TM) PMem modules have a consistent security state. .Table Security Check Events //// asciidoctor-pdf only renders as expected with .autowidth.stretch is used asciidoc does not recognize .autowidth and reports an error //// ifndef::backend-pdf[] [options="autowidth"] [role="stretch"] [cols="8,12,50,30"] endif::backend-pdf[] ifdef::backend-pdf[] [.autowidth.stretch] [cols="8,12,50,30"] endif::backend-pdf[] |=== |Code |Severity |Message |Arguments |800 |Info |The security check succeeded. | |801 |Info |The security check detected that there are no manageable PMem modules. | |802 |Warning |The security check detected that security settings are inconsistent [1]. a|[.text-left] . A comma separated list of the number of PMem modules in each security state |804 |Info |The security check detected that security is not supported on all PMem modules. | |805 |Error |An internal error caused the security check to abort. | |=== == Firmware Consistency and Settings Check Events This test group verifies that all PMem modules of a given subsystem device ID have consistent FW installed and other FW modifiable attributes are set in accordance with best practices. .Table Firmware Consistency and Settings Check Events //// asciidoctor-pdf only renders as expected with .autowidth.stretch is used asciidoc does not recognize .autowidth and reports an error //// ifndef::backend-pdf[] [options="autowidth"] [role="stretch"] [cols="8,12,50,30"] endif::backend-pdf[] ifdef::backend-pdf[] [.autowidth.stretch] [cols="8,12,50,30"] endif::backend-pdf[] |=== |Code |Severity |Message |Arguments |900 |Info |The firmware consistency and settings check succeeded. | |901 |Info |The firmware consistency and settings check detected that there are no manageable PMem modules. | |902 |Warning |The firmware consistency and settings check detected that firmware version on PMem modules [1] with subsystem device ID [2] is non-optimal, preferred version is [3]. a|[.text-left] . Comma separated list of PMem module UIDs . Subsystem device ID . Preferred firmware version |903 |Warning |The firmware consistency and settings check detected that PMem module [1] is reporting a non-critical media temperature threshold of [2] C which is above the fatal threshold [3] C. UID: [4] a|[.text-left] . PMem module Handle . Current media temperature threshold . Fatal media temperature threshold . PMem module UID |904 |Warning |The firmware consistency and settings check detected that PMem module [1] is reporting a non-critical controller temperature threshold of [2] C which is above the fatal threshold [3] C. UID: [4] a|[.text-left] . PMem module Handle . Current controller temperature threshold . Fatal controller temperature threshold . PMem module UID |905 |Warning |The firmware consistency and settings check detected that PMem module [1] is reporting a percentage remaining of [2]% which is below the recommended threshold [3]%. UID: [4] a|[.text-left] . PMem module Handle . Current percentage remaining threshold . Recommended percentage remaining threshold . PMem module UID |906 |Warning |The firmware consistency and settings check detected that PMem modules have inconsistent viral policy settings. | |910 |Error |An internal error caused the firmware consistency and settings check to abort. | |911 |Warning |The firmware consistency and settings check detected that PMem modules have inconsistent first fast refresh settings. | |=== ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-dump-debug-log.txt000066400000000000000000000060441440615110200256050ustar00rootroot00000000000000// Copyright (c) 2021, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-dump-debug-log(1) ======================== endif::manpage[] NAME ---- ipmctl-dump-debug-log - Dumps encoded firmware debug logs from specified PMem modules and optionally decodes to human readable text. SYNOPSIS -------- [listing] -- ipmctl dump [OPTIONS] -destination (file_prefix) [-dict (file)] -debug [TARGETS] [PROPERTIES] -- DESCRIPTION ----------- Dumps encoded firmware debug logs from specified PMem modules and optionally decodes to human readable text using a dictionary file. ifndef::os_build[] NOTE: For any non-functional PMem modules logs will be retrieved via SMBus. endif::os_build[] OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -lpmb:: Used to specify large transport payload size for the current invocation of ipmctl. -spmb:: Used to specify small transport payload size for the current invocation of ipmctl. NOTE: The -lpmb and -spmb options are mutually exclusive and may not be used together. TARGET ------ -destination (file_prefix):: The command will create files that use the given filename as a prefix and append the PMem module UID, PMem module handle, debug log source, and the appropriate file type (.bin for encoded logs, .txt for decoded logs) onto the end. [listing] -- file_prefix_Uid_Handle_logsource.[bin,txt] -- -dict (path):: Optional file path to the dictionary file. If supplied, the command will create both the binary debug log and a text file with decoded log data with the file prefix specified by -destination. Firmware dictionaries are lookup tables that match firmware codes to their text descriptions. When referenced, the dictionary will be used to substitute the provided codes with text, making the logs more human readable. Dictionaries are included in every firmware release under the format nlog_dict.X.X.X.XXXX.txt. -dimm [DimmIDs]:: Dumps the debug logs from the specified PMem modules. EXAMPLES -------- Dumps and decodes the debug log from PMem module 0x0001 and 0x0011 using the dictionary file. [listing] -- ipmctl dump -destination file_prefix -dict nlog_dict.txt -debug -dimm 0x0001,0x0011 -- LIMITATIONS ----------- To successfully execute this command, the specified PMem modules must be manageable by the host software. RETURN DATA ----------- Dumps the encoded and optionally decoded contents of all 3 firmware debug log sources for the specified PMem modules. Output file names are generated based on the -destination parameter above. SAMPLE OUTPUT ------------- [listing] -- Dumped media FW debug logs to file (file_prefix_8089-A1-1816-00000016_0x0001_media.bin) Decoded 456 records to file (file_prefix_8089-A1-1816-00000016_0x0001_media.txt) No spi FW debug logs found -- ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-dump-session.txt000066400000000000000000000035371440615110200254270ustar00rootroot00000000000000// Copyright (c) 2021, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-dump-session(1) ====================== endif::manpage[] NAME ---- ipmctl-dump-session - Dumps content captured during a recording session. SYNOPSIS -------- [listing] -- ipmctl dump [OPTIONS] -destination (path) -session -- DESCRIPTION ----------- Dumps content captured during a recording session. Captured content includes ACPI, SMBIOS tables, and FIS requests and responses. The resulting session file that is generated can be used to playback commands recorded on a real platform in a simulated environment, making it possible to debug issues offline. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. TARGET ------ -destination (path):: An absolute or relative path including the filename, where the contents of the active session will be copied to. -session:: Specifies to dump the contents associated with an active recording session. EXAMPLES -------- Dump the contents associated with the current active recording session to /tmp/session.pbr. [listing] -- ipmctl dump -destination /tmp/session.pbr -session -- LIMITATIONS ----------- To successfully execute this command, there must be an active recording session. RETURN DATA ----------- The resulting file includes, NFIT, PCAT, PMTT and SMBIOS tables that are used by IPMCTL to determine the PMem module topology. PMem module data that is transferred to/from PMem modules over the mailbox interface. SAMPLE OUTPUT ------------- [listing] -- Successfully dumped 1060619 bytes to file. -- ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-inject-error.txt000066400000000000000000000200671440615110200254010ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-inject-error(1) ====================== endif::manpage[] NAME ---- ipmctl-inject-error - Injects an error or clears a previously injected error SYNOPSIS -------- [listing] -- ipmctl set [OPTIONS] -dimm [TARGETS] [PROPERTIES] -- DESCRIPTION ----------- Injects an error or clears a previously injected error on one or more PMem module for testing purposes. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] TARGETS ------- -dimm [DimmIDs]:: Injects or clears an error on specific PMem modules by supplying one or more comma separated PMem module identifiers. The default is to inject the error on all manageable PMem modules. PROPERTIES ---------- This command only supports setting or clearing one type of error at a time. Clear:: * "1": Clears a previously injected error. This property must be combined with one of the other properties indicating the previously injected error to clear. Temperature:: Injects an artificial media temperature in degrees Celsius into the PMem module. The firmware that is monitoring the temperature of the PMem module will then be alerted and take necessary precautions to preserve the PMem module. The value is injected immediately and will override the firmware from reading the actual media temperature of the device and use this value instead which may cause adverse reactions by the firmware and result in an alert or log. NOTE: The injected temperature value will remain until the next reboot or until it is cleared. The media temperature is an artificial temperature and will not cause harm to the part. Although firmware actions due to improper temperature injections may cause adverse effects on the PMem module. + If the Critical Shutdown Temperature, or higher, is passed in, this may cause the PMem module firmware to perform a shutdown in order to preserve the part and data. + The temperature value will be ignored on clear. Poison:: The physical address to poison. + Poison is not possible for any address in the PM region if the PM region is locked. Injected poison errors are only triggered on a subsequent read of the poisoned address in which case an error log will be generated by the firmware, but no alerts will be sent. + This command can be used to clear non-injected poison errors. The data will be zeroed after clearing. There is no requirement to enable error injection prior to request to clear poison errors. + The caller is responsible for keeping a list of injected poison errors, in order to properly clear the injected errors afterwards. Simply disabling injection does not clear injected poison errors. Injected poison errors are persistent across power cycles and system resets. NOTE: System firmware (BIOS) will not read from any Intel(R) Optane(TM) PMem device addresses that are known to be poisoned. For any poisoned address, the first read may result in a hang/fault, but system firmware (BIOS) will mark this address as poisoned so subsequent attempts to read poisoned addresses will be rejected with an error. The result of such an error may prevent booting from a namespace that has poisoned data. NOTE: The address must be 256 byte aligned (e.g., 0x10000000, 0x10000100, 0x10000200...). PoisonType:: The type of memory to poison. One of: * "PatrolScrub": Injects a poison error at the specified address simulating an error found during a patrol scrub operation indifferent to how the memory is currently allocated. This is the default. * "MemoryMode": Injects a poison error at the specified address currently allocated in Memory Mode. * "AppDirect": Injects a poison error at the specified address currently allocated as App Direct. + NOTE: If the address to poison is not currently allocated as the specified memory type, an error is returned. PackageSparing:: - "1": Triggers an artificial package sparing. If package sparing is enabled and the PMem module still has spares remaining, this will cause the firmware to report that there are no spares remaining. NOTE: Injecting package sparing is not supported on Intel(R) Optane(TM) Persistent Memory 300 series modules. PercentageRemaining:: Injects an artificial percentage remaining value into the PMem module. This will cause the firmware to take appropriate action based on the value and if necessary generate an error log, an alert, and update the health status. FatalMediaError:: * "1": Injects a fake media fatal error which will cause the firmware to generate an error log and an alert. NOTE: When media fatal error is injected, BSR Media Disabled status bit will be set indicating media error, until the fatal error is cleared using disable trigger input parameter to clear this injected fatal error. NOTE: Injecting a Fatal Media error is unsupported on Windows*. Contact Microsoft* for assistance in performing this action. NOTE: When a fatal media error is cleared, A power cycle is needed for this operation to take effect. DirtyShutdown:: * "1": Injects an ADR failure resulting in dirty shutdown upon reboot. EXAMPLES -------- Sets the media temperature on all manageable PMem modules to 50 degrees Celsius. [listing] -- ipmctl set -dimm Temperature=50 -- Clears the injected media temperature on all manageable PMem modules. [listing] -- ipmctl set -dimm Clear=1 Temperature=1 -- Poison address 0x10000200 on PMem module 1234. [listing] -- ipmctl set -dimm 1234 Poison=0x10000200 -- Clears the injected poison of address 0x10000200 on PMem module 1234. [listing] -- ipmctl set -dimm 1234 Poison=0x10000200 Clear=1 -- Triggers an artificial package sparing on all manageable PMem modules. [listing] ipmctl set -dimm PackageSparing=1 Sets the life remaining percentage on all manageable PMem modules to 10%. [listing] -- ipmctl set -dimm PercentageRemaining=10 -- Clears the injected remaining life percentage on all manageable PMem modules. The value of PercentageRemaining is irrelevant. [listing] -- ipmctl set -dimm PercentageRemaining=10 Clear=1 -- Triggers an artificial ADR failure on all manageable PMem modules resulting in a dirty shutdown on each PMem module on the next reboot. [listing] -- ipmctl set -dimm DirtyShutdown=1 -- LIMITATIONS ----------- This command is available only when error injection is enabled on the PMem modules in the BIOS. To successfully execute this command, the specified PMem modules must be manageable by the host software. RETURN DATA ----------- For each PMem module, the CLI will indicate the status of the operation. If a failure occurs when injecting an error on multiple PMem modules, the process will continue with the remaining PMem modules. SAMPLE OUTPUT ------------- [listing] -- Set temperature on PMem module (DimmID): Success|Error (Code) - (Description) Clear injected temperature on PMem module (DimmID): Success|Error (Code) - (Description) -- [listing] -- Poison address (Address) on PMem module (DimmID): Success|Error (Code) - (Description) Clear injected poison of address (Address) on PMem module (DimmID): Success|Error (Code) - (Description) -- [listing] Trigger package sparing on PMem module (DimmID): Success|Error (Code) - (Description) Clear injected package sparing on PMem module (DimmID): Success|Error (Code) - (Description) [listing] -- Trigger a spare capacity alarm on PMem module (DimmID): Success|Error (Code) - (Description) Clear injected spare capacity alarm on PMem module (DimmID): Success|Error (Code) - (Description) -- [listing] -- Create a media fatal error on PMem module (DimmID): Success|Error (Code) - (Description) Clear injected media fatal error on PMem module (DimmID): Success|Error (Code) - (Description) -- ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-load-session.txt000066400000000000000000000025151440615110200253740ustar00rootroot00000000000000// Copyright (c) 2021, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-load-session(1) ====================== endif::manpage[] NAME ---- ipmctl-load-session - Loads content captured during a recording session. SYNOPSIS -------- [listing] -- ipmctl load [OPTIONS] -source (path) -session -- DESCRIPTION ----------- Loads content captured during a recording session into internal memory buffers. Captured content includes ACPI, SMBIOS tables, and FIS requests and responses. A loaded session can be executed using the 'start -session' command. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. TARGET ------ -source (path):: An absolute or relative path including the filename. -session:: Specifies that the source file contains recording data. EXAMPLES -------- Load the contents of a previously recorded session from /tmp/session.pbr. [listing] -- ipmctl load -source /tmp/session.pbr -session -- SAMPLE OUTPUT ------------- [listing] -- Successfully loaded 1060619 bytes to session buffer. -- ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-pbr-overview.txt000066400000000000000000000160011440615110200254160ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] PBR Overview(7) =============== NAME ---- ipmctl-pbr-overview - Describes concepts used in the PBR functionality of ipmctl DESCRIPTION ----------- endif::manpage[] Playback and Record (PBR) is a capability included to enable efficient reproduction and debug of issues a user may encounter. The capability is designed to capture the current state of the platform as it relates to PMem modules, and all interactions with the PMem module firmware. This data can then be stored in a file and sent to the development team for rapid reproduction and debug. The PBR file contains the following: * ACPI tables: NFIT, PCAT and PMTT * SMBIOS tables * Raw firmware command response data [role="lead"] *Theory of operation: Recording* . Start a recording session (start -session). ifndef::os_build[] . Manually unload the Intel(R) Optane(TM) Persistent Memory Driver. . Manually load the Intel(R) Optane(TM) Persistent Memory Driver. endif::os_build[] . Execute all commands to be included in session. . Save the recording to a file (dump -session). . Stop the recording session (stop -session). . Send PBR files to support personnel for analysis. ifndef::os_build[] NOTE: PBR functionality has some differences in the UEFI shell environment. General usage remains the same as OS shell usage, but the additional steps to unload and load the driver are required to capture driver initialization. endif::os_build[] [role="lead"] *Example Recording Sequence* To record CLI commands you begin by starting a recording session. [listing] -- # ipmctl start -session -mode record Setting to record mode. -- All commands executed from this point forward will be added to the recording session. [listing] -- # ipmctl show -dimm 1 Warning - Executing in recording mode! DimmID | Capacity | LockState | HealthState | FWVersion =============================================================== 0x0001 | 253.734 GiB | Disabled | Healthy | 02.01.00.1034 # ipmctl show -dimm -firmware Warning - Executing in recording mode! DimmID | ActiveFWVersion | StagedFWVersion ============================================ 0x0001 | 02.01.00.1034 | N/A 0x0101 | 02.01.00.1034 | N/A 0x1001 | 02.01.00.1034 | N/A 0x1101 | 02.01.00.1034 | N/A # ipmctl show -dimm 1 -sensor Warning - Executing in recording mode! DimmID | Type | CurrentValue ===================================================== 0x0001 | Health | Healthy 0x0001 | MediaTemperature | 38C 0x0001 | ControllerTemperature | 40C 0x0001 | PercentageRemaining | 100% 0x0001 | LatchedDirtyShutdownCount | 4 0x0001 | PowerOnTime | 10661690s 0x0001 | UpTime | 4138492s 0x0001 | PowerCycles | 46 0x0001 | FwErrorCount | 2 0x0001 | UnlatchedDirtyShutdownCount | 26 -- To preserve the session for later playback, dump the session to a file. [listing] -- # ipmctl dump -destination myrecording.pbr -session Warning - Executing in recording mode! Successfully dumped 101405 bytes to file. -- Remember to stop the session when you are done recording. NOTE: Stopping a session frees all recording data saved, which is why there is a verify prompt. To skip this verify prompt, use the -force option. NOTE: Session related commands are ignored by the recording/playback mechanism. [listing] -- # ipmctl stop -session Warning - Executing in recording mode! Stopping a session will free all recording content. Do you want to continue? [y/n] y Stopped PBR session. -- [role="lead"] *Theory of operation: Playback* . Load the recorded session (load -session). . Start playback to execute commands all at once or individually (start -session). . Debug as necessary. . Stop the playback session (stop -session). [role="lead"] *Example Playback Sequence* Load the existing PBR file. [listing] -- # ipmctl load -listing myrecording.pbr -session Successfully loaded 35175 bytes to session buffer. -- To examine the recorded command sequence, show the session. [listing] -- # ipmctl show -session TagID | Args ======================================= 0x0* | show -dimm 1 0x1 | show -dimm -firmware 0x2 | show -dimm 1 -sensor -- During playback, all the commands can be run at once or individually. To run all the commands at once, use playback mode. [listing] -- # ipmctl start -session -mode playback DimmID | Capacity | LockState | HealthState | FWVersion =============================================================== 0x0001 | 253.734 GiB | Disabled | Healthy | 02.01.00.1034 DimmID | ActiveFWVersion | StagedFWVersion ============================================ 0x0001 | 02.01.00.1034 | N/A 0x0101 | 02.01.00.1034 | N/A 0x1001 | 02.01.00.1034 | N/A 0x1101 | 02.01.00.1034 | N/A DimmID | Type | CurrentValue ===================================================== 0x0001 | Health | Healthy 0x0001 | MediaTemperature | 38C 0x0001 | ControllerTemperature | 40C 0x0001 | PercentageRemaining | 100% 0x0001 | LatchedDirtyShutdownCount | 4 0x0001 | PowerOnTime | 10661690s 0x0001 | UpTime | 4138492s 0x0001 | PowerCycles | 46 0x0001 | FwErrorCount | 2 0x0001 | UnlatchedDirtyShutdownCount | 26 -- To run the commands individually (one at a time), use playback_manual mode. This requires invoking the commands in the correct order - the same order they were recorded. To see which command is next, use 'show -session' and note the asterisk (*) denotes the command that will be executed next. To set the next command to be executed, use the -tag option. In this example, the command associated with tag 1 will be set as next. [listing] -- # ipmctl start -session -mode playback_manual -tag 1 Warning - Executing in playback mode! Setting to playback_manual mode. # ipmctl show -session Warning - Executing in playback mode! TagID | Args ======================================= 0x0 | show -dimm 1 0x1* | show -dimm -firmware 0x2 | show -dimm 1 -sensor -- Now the command 'show -dimm -firmware' can be run and the next command to be executed will advance to tag 2. [listing] -- # ipmctl show -dimm -firmware Warning - Executing in playback mode! DimmID | ActiveFWVersion | StagedFWVersion ============================================ 0x0001 | 02.01.00.1034 | N/A 0x0101 | 02.01.00.1034 | N/A 0x1001 | 02.01.00.1034 | N/A 0x1101 | 02.01.00.1034 | N/A # ipmctl show -session Warning - Executing in playback mode! TagID | Args ======================================= 0x0 | show -dimm 1 0x1 | show -dimm -firmware 0x2* | show -dimm 1 -sensor -- When done with the playback session, use 'stop -session' to disable the playback mode and resume normal operation. [listing] -- # ipmctl stop -session Warning - Executing in playback mode! Stopping a session will free all recording content. Do you want to continue? [y/n] y Stopped PBR session. -- ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-show-cap.txt000066400000000000000000000047461440615110200245250ustar00rootroot00000000000000// Copyright (c) 2021, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-cap(1) ================== endif::manpage[] NAME ---- ipmctl-show-cap - Shows the current Command Access Policy restrictions. SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -cap [TARGETS] -- DESCRIPTION ----------- Shows the current Command Access Policy restrictions. This is a list of Opcode:SubOpcode and the corresponding mailboxes the commands are restricted, if any. Each command may be restricted to None, BIOS only, SMBus only, or BIOS and SMBus only. None indicates that Host, BIOS, and SMBus mailbox access is allowed. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] TARGETS ------- -dimm [DimmIDs]:: Restricts output to specific PMem modules by supplying one or more comma separated PMem module identifiers. The default is to display all manageable PMem modules. EXAMPLES -------- Lists restrictions for all PMem modules installed in the system [listing] -- ipmctl show -cap -- Lists restrictions for PMem module 0x1001 [listing] -- ipmctl show -dimm 0x1001 -cap -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software. RETURN DATA ----------- The default behavior is to return a table which indicates the restrictions enforced by Command Access Policy (CAP). DimmID:: The default display of PMem module identifiers. One of: * UID: Use the DimmUID attribute as defined in the command <>. * HANDLE: Use the DimmHandle attribute as defined in the command <>. This is the default. Opcode:: The Opcode for a command. SubOpcode:: The SubOpcode for a command. Restriction:: Text describing which mailboxes are restricted for Opcode:SubOpcode combination. One of: * None * BIOS only * SMBus only * BIOS and SMBus only * Management only * Management and BIOS only * Management and SMBus only * Management, BIOS and SMBus only * Unsupported ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-show-cel.txt000066400000000000000000000052221440615110200245130ustar00rootroot00000000000000// Copyright (c) 2021, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-cel(1) ================== endif::manpage[] NAME ---- ipmctl-show-cel - Shows the current PMem module firmware Command Effect Log. SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -cel [TARGETS] -- DESCRIPTION ----------- Retrieves the PMem module command effect log and presents the response as a list of DimmID, Opcode, SubOpcode and PMem module firmware command effects. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -lpmb:: Used to specify large transport payload size for the current invocation of ipmctl. -spmb:: Used to specify small transport payload size for the current invocation of ipmctl. NOTE: The -lpmb and -spmb options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] TARGETS ------- -dimm [DimmIDs]:: Restricts output to specific PMem modules by supplying one or more comma separated PMem module identifiers. The default is to display all manageable PMem modules. EXAMPLES -------- Lists command effects for all PMem modules installed in the system [listing] -- ipmctl show -cel -- Lists command effects for PMem module 0x1001 [listing] -- ipmctl show -dimm 0x1001 -cel -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software. RETURN DATA ----------- The default behavior is to return a table which lists the command effects per Opcode and SubOpcode. DimmID:: The default display of PMem module identifiers. One of: * UID: Use the DimmUID attribute as defined in the command <>. * HANDLE: Use the DimmHandle attribute as defined in the command <>. This is the default. Opcode:: The Opcode for a command. SubOpcode:: The SubOpcode for a command. CE Description:: A comma separated list that includes one or more of: * NE: No Effect * SSC: Security State Change * DCC: DIMM Configuration Change after reboot * IDCC: Immediate DIMM Configuration Change * QIO: Quiesce All IO * IDDC: Immediate DIMM Data Change * TM: Test Mode * DM: Debug Mode * IDPC: Immediate DIMM Policy Change ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-show-error-log.txt000066400000000000000000000067371440615110200256740ustar00rootroot00000000000000// Copyright (c) 2021, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-error-log(1) ======================== endif::manpage[] NAME ---- ipmctl-show-error-log - Shows thermal or media errors on the specified PMem modules SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -error (Thermal|Media) [TARGETS] [PROPERTIES] -- DESCRIPTION ----------- Shows thermal or media errors on the specified PMem modules. OPTIONS ------- -a:: -all:: Shows all attributes. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -lpmb:: Used to specify large transport payload size for the current invocation of ipmctl. -spmb:: Used to specify small transport payload size for the current invocation of ipmctl. NOTE: The -lpmb and -spmb options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] TARGETS ------- -dimm [DimmIDs]:: Filter output to specific PMem modules by optionally supplying one or more comma separated PMem module identifiers. The default is to include all manageable PMem modules. PROPERTIES ---------- SequenceNumber:: Error log entries are stored with a sequence number starting with 1 and rolling over back to 1 after 65535. Limit the error log entries returned by providing a sequence number. Only errors with a sequence number equal to or higher than provided will be returned. The default is 1. Level:: Severity level of errors to be fetched. One of: * "High": High severity errors (Default) * "Low": Low severity errors Count:: Max number of error entries to be fetched and printed. The default is 8 for media errors and 16 for thermal errors. EXAMPLES -------- Show all high thermal error log entries [listing] -- ipmctl show -error Thermal Level=High -- Show all low media error log entries [listing] -- ipmctl show -error Media Level=Low -- LIMITATIONS ----------- To successfully execute this command, the specified PMem modules must be manageable by the host software. In addition, for PMem modules with firmware version 2.3 and above, errors due to AIT operations are no longer exposed. RETURN DATA ----------- Prints errors of the specified type for the specified PMem modules. If no errors are found, the following message will be printed: [listing] -- No errors found on PMem module (DimmID) -- SAMPLE OUTPUT ------------- [listing] -- DimmID | System Timestamp | Error Type ============================================================= 0x0001 | 01/01/1970 00:00:20 | 0x04 - Locked/Illegal Access 0x0001 | 01/01/1970 00:00:21 | 0x04 - Locked/Illegal Access Show error executed successfully -- [listing] -- ---DimmID=0x0001--- ---Error=Thermal System Timestamp=08/09/2021 05:12:27 Temperature=84 Reported=High Temperature Type=Media Temperature Sequence Number=1 -- [listing] -- ---DimmID=0x0001--- ---Error=Media System Timestamp=01/01/1970 00:00:22 Error Type=0x04 - Locked/Illegal Access Transaction Type=0x0B - CSR Write Error Flags=0x02 - DPA Valid DPA=0x0000000000020A40 PDA=N/A Range=4B Sequence Number=1 -- ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-show-pcd.txt000066400000000000000000000041251440615110200245170ustar00rootroot00000000000000// Copyright (c) 2021, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-pcd(1) ================== endif::manpage[] NAME ---- ipmctl-show-pcd - Shows the platform configuration data for one or more PMem modules SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -pcd [TARGETS] -- DESCRIPTION ----------- Shows the platform configuration data for one or more PMem modules. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -lpmb:: Used to specify large transport payload size for the current invocation of ipmctl. -spmb:: Used to specify small transport payload size for the current invocation of ipmctl. NOTE: The -lpmb and -spmb options are mutually exclusive and may not be used together. TARGETS ------- -dimm [DimmIDs]:: Restricts output to the platform configuration data on specific PMem modules by supplying one or more comma separated PMem module identifiers. The default is to display the platform configuration data for all manageable PMem modules. -pcd [Config|LSA]:: Restricts output to a specific partition of the platform configuration data. The default is to display both. One of: - Config - Configuration management information - LSA - Namespace label storage area EXAMPLES -------- Shows the configuration information from the platform configuration data for all manageable PMem modules. [listing] -- ipmctl show -dimm -pcd -- Shows the configuration information from the platform configuration data for PMem module 0x1. [listing] -- ipmctl show -dimm -pcd Config -- LIMITATIONS ----------- The specified PMem modules must be manageable by the host software. RETURN DATA ----------- Returns the formatted data from the requested platform configuration data for the specified PMem modules for debugging and troubleshooting purposes. ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-show-session.txt000066400000000000000000000025731440615110200254410ustar00rootroot00000000000000// Copyright (c) 2021, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-session(1) ====================== endif::manpage[] NAME ---- ipmctl-show-session - Displays the playback and record (PBR) command history of a session. SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -session -- DESCRIPTION ----------- Displays the command history of a session. Also displays which command to execute next during 'playback_manual' mode (see 'start -session'). OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. TARGET ------ -session:: Specifies to show the command history of loaded/active session. EXAMPLES -------- Show the contents of the loaded/active session. [listing] -- ipmctl show -session -- LIMITATIONS ----------- A session must be loaded or active prior to executing this command. A session may be loaded via the 'load -session' command or made active via the 'start -session' command. SAMPLE OUTPUT ------------- [listing] -- TagID | RC | Args ============================ 0x0* | 0 | show -sensor 0x1 | 0 | show -dimm -- ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-show-smbios.txt000066400000000000000000000027321440615110200252470ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-smbios(1) ===================== endif::manpage[] NAME ---- ipmctl-show-smbios - Shows the system SMBIOS tables related to the PMem modules. SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -smbios [TARGETS] -- DESCRIPTION ----------- Shows the system SMBIOS tables related to the PMem modules in the system. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. TARGETS ------- -smbios [17]:: The SMBIOS table to display. Limit to a specific table by supplying the table type (from the SMBIOS specification). One of: - 17 - Memory Device (Type 17) -dimm [DimmIDs]:: Restricts output to specific PMem modules by supplying the PMem module target and one or more comma separated PMem module identifiers. The default is to display all PMem modules. EXAMPLES -------- Show the type 17 SMBIOS table for all PMem module [listing] -- ipmctl show -smbios 17 -- LIMITATIONS ----------- None RETURN DATA ----------- Dumps the contents of the SMBIOS tables for each PMem module. Refer to the SMBIOS specification Memory Device (Type 17) section for more information. ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-show-system.txt000066400000000000000000000027021440615110200252740ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-system(1) ===================== endif::manpage[] NAME ---- ipmctl-show-system - Shows the system ACPI tables related to the PMem modules SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -system -- DESCRIPTION ----------- Shows the system ACPI tables related to the PMem modules in the system. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. TARGETS ------- -system [NFIT|PCAT|PMTT]:: The system ACPI tables to display. By default NFIT, PCAT and PMTT tables are displayed. One of: * "NFIT" - The NVDIMM Firmware Interface Table * "PCAT" - The Platform Capabilities Table * "PMTT" - The Platform Memory Topology Table See the ACPI specification for detailed information about the ACPI tables. EXAMPLES -------- Show the ACPI NFIT [listing] -- ipmctl show -system NFIT -- RETURN DATA ----------- Returns the formatted data from the requested ACPI tables and their sub-tables. Refer to the ACPI specification for detailed information about the format of the ACPI tables. NOTE: All data is presented in ACPI little endian format. ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-start-diagnostic.txt000066400000000000000000000102641440615110200262530ustar00rootroot00000000000000// Copyright (c) 2019, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-run-diagnostic(1) ======================== endif::manpage[] NAME ---- ipmctl-start-diagnostic - Starts a diagnostic test ifndef::manpage[] For a complete list of diagnostic test results, refer to addendum section <>. endif::manpage[] SYNOPSIS -------- [listing] -- ipmctl start [OPTIONS] -diagnostic [TARGETS] -- DESCRIPTION ----------- Starts a diagnostic test. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -lpmb:: Used to specify large transport payload size for the current invocation of ipmctl. -spmb:: Used to specify small transport payload size for the current invocation of ipmctl. NOTE: The -lpmb and -spmb options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] TARGETS ------- -diagnostic [Quick|Config|Security|FW]:: Start a specific test by supplying its name. All tests are run by default. One of: * "Quick" - This test verifies that the PMem module host mailbox is accessible and that basic health indicators can be read and are currently reporting acceptable values. * "Config" - This test verifies that the BIOS platform configuration matches the installed hardware and the platform configuration conform to best known practices. * "Security" - This test verifies that all PMem modules have a consistent security state. It is a best practice to enable security on all PMem modules rather than just some. * "FW" - This test verifies that all PMem modules of a given model have consistent FW installed and other FW modifiable attributes are set in accordance with best practices. + Note that the test does not have a means of verifying that the installed FW is the optimal version for a given PMem module model just that it has been consistently applied across the system. -dimm [DimmIDS]:: Starts a diagnostic test on specific PMem modules by optionally supplying one or more comma separated PMem module identifiers. The default is to start the specified tests on all manageable PMem modules. Only valid for the Quick diagnostic test. EXAMPLES -------- Starts all diagnostics. [listing] -- ipmctl start -diagnostic -- Starts the quick check diagnostic on PMem module 0x0001. [listing] -- ipmctl start -diagnostic Quick -dimm 0x0001 -- LIMITATIONS ----------- If a PMem module is unmanageable, then Quick test will report the reason, while Config, Security and FW tests will skip unmanageable PMem modules. RETURN DATA ----------- Each diagnostic generates one or more log messages. A successful test generates a single log message per PMem module indicating that no errors were found. A failed test might generate multiple log messages each highlighting a specific error with all the relevant details. Each log contains the following information. Test:: The test name along with overall execution result. One of: * "Quick" * "Config" * "Security" * "FW" State:: The collective result state for each test. One of: * "Ok" * "Warning" * "Failed" * "Aborted" Message:: The message indicates the status of the test. One of: * "Ok" * "Failed" SubTestName:: The subtest name for given Test. [cols="1,2a", options="header"] |=== |Test Name |Valid SubTest Names |Quick | * Manageability * Boot status * Health |Config | * PMem module specs * Duplicate PMem module * System Capability * Namespace LSA * PCD |Security | * Encryption status * Inconsistency |FW | * FW Consistency * Viral Policy * Threshold check * System Time |=== State:: The severity of the error for each sub-test displayed with SubTestName. One of: - "Ok" - "Warning" - "Failed" - "Aborted" ifdef::manpage[] include::ipmctl-diagnostic-events.txt[leveloffset=+1] endif::manpage[]ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-start-session.txt000066400000000000000000000062161440615110200256140ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-start-session(1) ======================= endif::manpage[] NAME ---- ipmctl-start-session - Starts a recording or playback session. SYNOPSIS -------- [listing] -- ipmctl start [OPTIONS] -session -mode (record|playback|playback_manual) [TARGETS] -- DESCRIPTION ----------- Starts a recording or playback session. The recording session records the platform's ACPI NFIT, PCAT, PMTT tables, SMBIOS tables, and FIS mailbox transactions that occur during the recording session. The normal use-case would be to start a recording session, execute commands (e.g., create -goal, show -sensors, etc.) to be recorded, dump the recorded session using the <> command, followed by stopping the session using the <> command. The "dumped" session can then be loaded and "played" back on any platform that can execute the ipmctl tool. The playback session has two modes: 'playback' and 'playback_manual'. The 'playback' mode will automatically execute all commands that were previously recorded. The 'playback_manual' mode allows commands to be executed one at a time in a manual fashion. If a tag is given, the playback will begin at the command that corresponds with the tagID. Note, the <> command displays the order and commands to execute, where the '*' denotes which command to execute next. OPTIONS ------- -f:: -force:: Do not warn the user that starting a new session terminates an active recording session resulting in deleting recorded content. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. TARGET ------ -session:: Specifies to start a session. -mode (record|playback|playback_manual):: The session modes supported. One of: - "record" - records data associated with command execution - "playback" - automatically executes commands previously recorded - "playback_manual" - enables manual execution of commands previously recorded -tag [tagID]:: Specifies the starting command by tagID. Only available with "playback" and "playback_manual" mode. EXAMPLES -------- Start a recording session. [listing] -- ipmctl start -session -mode record -- Automatically execute commands in a session. [listing] -- ipmctl start -session -mode playback -- Allow for manual execution of commands in playback mode [listing] -- ipmctl start -session -mode playback_manual -- LIMITATIONS ----------- Recordings should be played back on the same IPMCTL version that created the recording. Recordings taken in UEFI should be played back in the UEFI environment (simulated or real). Recordings taken in an OS are binary compatible with other OS versions of IPMCTL (i.e., recording taken in Linux* can be played back in Windows*). RETURN DATA ----------- In 'playback' mode the output will be a concatenation of the output from each played back command. ipmctl-03.00.00.0485/Documentation/ipmctl/Debug/ipmctl-stop-session.txt000066400000000000000000000024611440615110200254420ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-stop-session(1) ====================== endif::manpage[] NAME ---- ipmctl-stop-session - Stops the active playback or recording session. SYNOPSIS -------- [listing] -- ipmctl stop [OPTIONS] -session -- DESCRIPTION ----------- Stops the active playback or recording session. OPTIONS ------- -f:: -force:: Do not warn the user that stopping a new session terminates an active recording session resulting in deleting recorded content. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. TARGET ------ -session:: Specifies to stop a session. EXAMPLES -------- Stop the current session. [listing] -- ipmctl stop -session -- LIMITATIONS ----------- A session must be already started. SAMPLE OUTPUT ------------- [listing] -- Successfully dumped 1060619 bytes to file. Warning - Executing in playback mode! Stopping a session will free all recording content. Do you want to continue? [y/n] y Stopped PBR session. -- ipmctl-03.00.00.0485/Documentation/ipmctl/Discovery/000077500000000000000000000000001440615110200216635ustar00rootroot00000000000000ipmctl-03.00.00.0485/Documentation/ipmctl/Discovery/ipmctl-show-dimm.txt000066400000000000000000000625521440615110200256300ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-dimm(1) =================== endif::manpage[] NAME ---- ipmctl-show-dimm - Shows information about one or more PMem modules SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -dimm [TARGETS] -- DESCRIPTION ----------- Shows information about one or more PMem modules. OPTIONS ------- -a:: -all:: Shows all attributes. NOTE: The all and display options are exclusive and may not be used together. -d (attributes):: -display (attributes):: Filters the returned attributes by explicitly specifying a comma separated list of any of the attributes defined in the Return Data section. NOTE: The all and display options are exclusive and may not be used together. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] -u (B|MB|MiB|GB|GiB|TB| TiB):: -units (B|MB|MiB|GB|GiB|TB| TiB):: Changes the units that capacities are displayed in for this command. One of: bytes (B), megabytes (MB), mebibytes (MiB), gigabytes (GB), gibibytes (GiB), terabytes (TB) or tebibytes (TiB). TARGETS ------- -dimm [DimmIDs]:: Restricts output to specific PMem modules by supplying the PMem module target and one or more comma separated PMem module identifiers. The default is to display all PMem modules. -socket [SocketIDs]:: Restricts output to the PMem modules installed on specific sockets by supplying the socket target and one or more comma separated socket identifiers. The default is to display all sockets. NOTE: If ACPI PMTT table is not present, then DDR memory will not be displayed in the filtered socket list. EXAMPLES -------- Lists a few key fields for each PMem module. [listing] -- ipmctl show -dimm -- Lists all properties for PMem module 0x0001. [listing] -- ipmctl show -a -dimm 0x0001 -- Retrieves specific properties for each PMem module. [listing] -- ipmctl show -d HealthState,LockState -dimm -- RETURN DATA ----------- The default behavior is to display a table with the default attributes listed below; applying options changes the output to a more detailed format. Limited information (noted in the table below) is applicable if the PMem module is not manageable by the software as indicated by the "ManageabilityState" property. NOTE: Some data is endian swapped for human readability. DimmID:: (Default) The PMem module identifier. Capacity:: (Default) The usable capacity of the PMem module as reported by the firmware. HealthState:: (Default) Overall PMem module health. One of: * Healthy * Noncritical: Maintenance may be required. * Critical: Features or performance are degraded due to failure. * Fatal: Critical internal state failure (DPA Failure, Internal Buffer Failure, AIT Failure, etc.) is non-recoverable and data loss has occurred or is imminent. In this case, the firmware will disable the media and access to user data and operations that require use of the media will fail. * Non-functional: The PMem module is detected and manageable, though some commands and capabilities may be limited. The PMem module has limited communication or another error preventing complete functionality. Common causes include: - DDRT memory interface training failure - Expected region mapping to SPA range unable to be found * Unmanageable: The PMem module has an incompatible firmware API version or hardware revision or is unresponsive (possibly due to a communication interface failure or a firmware/hardware error). * Unknown: Unable to determine the PMem module health state. Refer to _HealthStateReason_ for _HealthState_ details. HealthStateReason:: Indicates why the PMem module is in the current Health State. One or more of: * None * Percentage Remaining less than 1% * Package Sparing occurred * CAP Self-Test warning * Percentage Remaining is 0 * Die Failure * AIT DRAM disabled * CAP Self-Test failure * Critical internal state failure * Performance degraded * CAP Self-Test communication failure + See the _Intel(R) Optane(TM) DC Persistent Memory Module Firmware Interface Specification_, document number 556488, Section 4.8 SMART and Health. InterfaceFormatCode:: A comma-delimited list of the JEDEC standard format interface codes for the PMem module where each code is formatted as: code (JEDEC Description or "Unknown"). ManageabilityState:: Ability of the PMem module host software to manage the PMem module. Manageability is determined by the interface format code, the vendor identifier, device identifier and the firmware API version. One of: * Manageable: The PMem module is manageable by the software. * Unmanageable: The PMem module is not supported by this version of the software. PopulationViolation:: Memory populations are evaluated based on the "Enforce Population POR" setup option in UEFI Firmware. If enforcement of POR populations is selected, then some PMem module memory may be in population violation. See <> for details. One of: * Yes: The PMem module is in population violation. * No: The PMem module is not in population violation. PhysicalID:: The PMem module physical identifier (i.e., SMBIOS Type 17 handle). DimmHandle:: The PMem module handle formatted as 0xABCD. * A = Socket * B = Memory Controller * C = Channel * D = Slot DimmUID:: The unique identifier of the PMem module formatted as VVVV-ML-MMYYSNSNSNSN or VVVV-SNSNSNSN (if the manufacturing information is not available) where: * VVVV = VendorID * ML = ManufacturingLocation * MMYY = ManufacturingDate * SNSNSNSN = SerialNumber SocketID:: The processor socket identifier (i.e., NUMA node) where the PMem module is installed. MemControllerID:: The associated memory controller identifier. ChannelID:: The associated channel. ChannelPos:: The PMem module position in the channel. MemoryType:: The memory type. One of: * Unknown * Logical Non-Volatile Device Manufacturer:: The manufacturer name of the PMem module. VendorID:: The vendor identifier of the PMem module. This value is presented in big endian format. DeviceID:: The device identifier of the PMem module. This value is presented in big endian format. RevisionID:: The revision identifier of the PMem module. SubsystemVendorID:: The vendor identifier of the non-volatile memory subsystem controller. This value is presented in big endian format. SubsystemDeviceID:: The device identifier of the non-volatile memory subsystem controller. SubsystemRevisionID:: The revision identifier of the non-volatile memory subsystem controller retrieved from NFIT. This field uses a different encoding than ControllerRevisionID. ManufacturingInfoValid:: If the manufacturing location and date are valid. One of: * 0: Not valid * 1: Valid ManufacturingLocation:: The manufacturing location assigned by the vendor or "N/A" if ManufacturingInfoValid is 0. ManufacturingDate:: The manufacturing date assigned by the vendor or "N/A" if ManufacturingInfoValid is 0. SerialNumber:: The serial number assigned by the vendor. This value is presented in big endian format. PartNumber:: The part number assigned by the vendor DeviceLocator:: A string describing the physically labeled socket or board position where the memory device is located from the SMBIOS Type 17 Memory Device table. BankLabel:: A string that identifies the physically labeled bank where the memory device is located from the SMBIOS Type 17 Memory Device table. DataWidth:: The width in bits used to store user data from the SMBIOS Type 17 Memory Device table. TotalWidth:: The width in bits for data and error correction and/or data redundancy from the SMBIOS Type 17 Memory Device table. Speed:: The maximum capable speed of the device in megatransfers per second (MT/s) from the SMBIOS Type 17 Memory Device table. FormFactor:: The PMem module form factor (i.e., SMBIOS Type 17 Memory Device Form Factor). One of: * Unknown * DIMM * SODIMM LockState:: The current security state of the persistent memory on the PMem module. One or more of: * Unknown - The security state cannot be determined (e.g., when the PMem module is not manageable by the software). * Disabled - Security is not enabled. * Unlocked - Security is enabled and unlocked. * Locked - Security is enabled and locked. * Frozen - A reboot is required to change the security state. * Exceeded - The passphrase limit has been reached. A power cycle is required to change the security state. * MP Exceeded - The master passphrase limit has been reached. A power cycle is required to change the security state. * Not Supported - Security is not supported on the PMem module. SVNDowngrade:: The Opt-in value of Security Version Number (SVN) Downgrade security opt-in feature for PMem module. One of : * Unknown * Disabled * Enabled SecureErasePolicy:: The Opt-in value of Secure erase policy opt-in feature for PMem module. One of : * Unknown * No Master Passphrase * Master Passphrase Enabled S3ResumeOptIn:: The Opt-in value of S3 Resume security opt-in feature for PMem module. One of : * Unknown * UnsecureS3 * SecureS3 FwActivateOptIn:: The Opt-in value of Fw Activate security opt-in feature for PMem module. One of : * Unknown * Disabled * Enabled FWVersion:: (Default) The BCD-formatted revision of the active firmware in the format PN.RN.SV.bbbb where: * PN = 2-digit product number * RN = 2-digit revision number * SN = 2-digit security revision number * bbbb = 4-digit build version Value may be N/A if the PMem module is not manageable by the software. FWAPIVersion:: The firmware supported interface revision in the format aa.bb where: * aa = 2-digit major version * bb = 2-digit minor version + The firmware interface is intended to be backwards compatible. Therefore, the host software allows management of PMem modules where this version is less than or equal to the version stored in the host software. Value may be N/A if the PMem module is not manageable by the software. *The following information is only applicable when the PMem module is manageable by the software as indicated by the "ManageabilityState".* FWActiveAPIVersion:: The firmware interface revision locked in the BIOS API handshake in the format aa.bb where: * aa = 2-digit major version * bb = 2-digit minor version + Value may be N/A if the PMem module is not manageable by the software. ManufacturerID:: The manufacturer identifier of the PMem module. This value is presented in big endian format. ControllerRevisionID:: The controller stepping and revision ID retrieved from the controller FW. This field uses a different encoding than SubsystemRevisionID. IsNew:: Whether or not the PMem module is incorporated with the rest of the PMem module in the system. One of: * 0: Configured * 1: The PMem module requires configuration. MemoryCapacity:: Usable PMem module Memory Mode capacity. AppDirectCapacity:: Usable PMem module App Direct capacity. UnconfiguredCapacity:: PMem module capacity that is inaccessible because it is not mapped into the system physical address space. InaccessibleCapacity:: PMem module capacity that is inaccessible due to: * Licensing issue * Platform configuration prevents accessing this capacity. For example, MemoryCapacity is configured and available on a PMem module but MemoryMode is not enabled by BIOS. ReservedCapacity:: PMem module capacity reserved for proper alignment. AvgPowerLimit:: If the PMem module firmware power management policy is enabled, the power limit in mW used for average power. Refer to FIS for allowable range and default value. MemoryBandwidthBoostFeature:: Returns if the Intel(R) Memory Bandwidth Boost Feature is currently enabled or not. One of: * 0x0: Disabled * 0x1: Enabled MemoryBandwidthBoostMaxPowerLimit:: The power limit used for limiting the Intel(R) Memory Bandwidth Boost Feature's power consumption [mW]. MemoryBandwidthBoostAveragePowerTimeConstant:: The value used as a base time window for average power throttle [ms]. This range can be checked in the Max Intel(R) Memory Bandwidth Boost Average Time Constant and Average Power Time Constant Step from the <> command. * Default: 15000 ms MaxAveragePowerLimit:: Maximum average power limit [mW] supported by the PMem module. MaxMemoryBandwidthBoostMaxPowerLimit:: Maximum Intel(R) Memory Bandwidth Boost Power value [mW] that can be set for the PMem module. Will return 0 if unsupported by current FIS. MaxMemoryBandwidthBoostAveragePowerTimeConstant:: This field returns the maximum supported value of the Intel(R) Memory Bandwidth Boost Average Power Time Constant [ms]. MemoryBandwidthBoostAveragePowerTimeConstantStep:: This field returns the increments in milliseconds allowed by the firmware when setting the Intel(R) Memory Bandwidth Boost Average Power Time Constant. MaxAveragePowerReportingTimeConstant:: This field returns the maximum supported value of the Reporting Average Power Time Constant in milliseconds that can be set in the <> command. AveragePowerReportingTimeConstantStep:: This field returns the increments in milliseconds allowed by the firmware when setting the Average Power Reporting Time Constant using the <> command. AveragePower:: This field returns the average power in milliwatts that each PMem module consumes over the Average Power Reporting Time Constant. Average12vPower:: This field returns the 12V average power in milliwatts that each PMem module consumes over the Average Power Reporting Time Constant. Available for FW API versions < 3.0 Average1_2vPower:: This field returns the 1.2V average power in milliwatts that each PMem module consumes over the Average Power Reporting Time Constant. Available for FW API versions < 3.0 PackageSparingCapable:: Whether or not the PMem module supports package sparing. One of: * 0: False * 1: True PackageSparingEnabled:: Whether or not the PMem module package sparing policy is enabled. One of: * 0: Disabled * 1: Enabled PackageSparesAvailable:: The number of spare devices available for package sparing. LatchedLastShutdownStatus:: The status of the last shutdown of the PMem module. One or more of: * Unknown: The last shutdown status cannot be determined. * PM ADR Command Received: Power management ADR command received. * PM S3 Received: Power management S3 command received. * PM S5 Received: Power management S5 command received. * DDRT Power Fail Command Received: DDR power fail command received. * PMIC 12V/DDRT 1.2V Power Loss (PLI) * PM Warm Reset Received: Power management warm reset received. * Thermal Shutdown Received: Thermal shutdown triggered. * Controller’s FW State Flush Complete: Flush Completed. * Viral Interrupt Received: Viral interrupt received. * Surprise Clock Stop Received: Surprise clock stop received. * Write Data Flush Complete: Write data flush completed. * PM S4 Received: Power management S4 command received. * PM Idle Received: Power management idle received. * SRE Clock Stop Received: Self-Refresh Entry clock stop received. * DDRT Surprise Reset Received: Surprise reset received. * Extended Flush Not Complete. * Extended Flush Complete. * Sx Extended Flush Not Complete. * Sx Extended Flush Complete. UnlatchedLastShutdownStatus:: The status of the last shutdown status of the PMem module. It contains the same fields as the Latched Last Shutdown Status, with the only difference that the LSS details on a dirty shutdown are logged, even if the Latch System Shutdown Status was not enabled. One or more of: * Unknown: The last shutdown status cannot be determined. * PM ADR Command Received: Power management ADR command received. * PM S3 Received: Power management S3 command received. * PM S5 Received: Power management S5 command received. * DDRT Power Fail Command Received: DDR power fail command received. * PMIC 12V/DDRT 1.2V Power Loss (PLI) * PM Warm Reset Received: Power management warm reset received. * Thermal Shutdown Received: Thermal shutdown triggered. * Controller’s FW State Flush Complete: Flush Completed. * Viral Interrupt Received: Viral interrupt received. * Surprise Clock Stop Received: Surprise clock stop received. * Write Data Flush Complete: Write data flush completed. * PM S4 Received: Power management S4 command received. * PM Idle Received: Power management idle received. * SRE Clock Stop Received: Self-Refresh Entry clock stop received. * DDRT Surprise Reset Received: Surprise reset received. * Extended Flush Not Complete. * Extended Flush Complete. * Sx Extended Flush Not Complete. * Sx Extended Flush Complete. ThermalThrottleLossPercent:: The average performance loss percentage due to thermal throttling in current boot of the PMem module. LastShutdownTime:: The time the system was last shut down. ModesSupported:: A list of the modes supported by the PMem module. Refer to the command <> to determine the modes supported by the platform. One or more of: * Memory Mode: PMem modules act as system memory under the control of the operating system. In Memory Mode, any DDR in the platform will act as a cache working in conjunction with the PMem module. * App Direct: PMem modules and DDR act as independent memory resources under direct load/store control of the application. SecurityCapabilities:: The security features supported by the PMem module. Zero or more of: * Encryption: The PMem module supports persistent memory encryption by setting a passphrase. * Erase: The PMem module is erasable. MasterPassphraseEnabled:: This property indicates if master passphrase is enabled. If it is disabled, then it cannot be enabled. One of: * 0: Disabled - Cannot be enabled. * 1: Enabled - Master passphrase can be changed. Cannot be disabled. ConfigurationStatus:: The status of the PMem module memory configuration. One of: * Valid: The configuration is valid. * Not Configured: The PMem module has not been configured. * Failed - Bad configuration: The configuration is corrupt. * Failed - Broken interleave: This PMem module is part of an interleave set that is not complete. * Failed - Reverted: The configuration failed and was reverted to the last known good configuration. * Failed - Unsupported: The configuration is not compatible with the installed BIOS. * Unknown: The configuration cannot be determined. SKUViolation:: The configuration of the PMem module is unsupported due to a license issue. One of: * 0: False * 1: True ARSStatus:: The address range scrub (ARS) operation status for the PMem module. The status is a reflection of the last requested ARS, but not necessarily within the current platform power cycle. One of: * Unknown - The ARS operation status cannot be determined. * Not started - An ARS operation has not started. * In progress - An ARS operation is currently in progress. * Completed - The last ARS operation has completed. * Aborted - The last ARS operation was aborted. * Error - An ARS operation failed due to some error. OverwriteStatus:: The overwrite PMem module operation status for the PMem module. One of: * Unknown - The overwrite PMem module operation status cannot be determined. This may occur if the status gets overwritten due to a different long operation running on this PMem module. * Not started - An overwrite PMem module operation was not started on the last boot. * In progress - An overwrite PMem module operation is currently in progress. * Completed - An overwrite PMem module operation completed and a reboot is required to use the PMem module. * Aborted - The last overwrite PMem module operation was aborted. * Error - An overwrite PMem module operation failed due to some error. AveragePowerReportingTimeConstant:: The value, in milliseconds, used to determine the time constant for reporting the average power consumption measurements. Can be set to a value between 100 and 12000, by increments of 100. The default value is 1000. ViralPolicy:: Whether viral policies are enabled on the PMem module. One of: * 0: Disabled - This is the default. * 1: Enabled - The persistent memory on the PMem module will be put into read-only mode if the host operating system software detects an uncorrectable error situation and indicates a viral state in order to prevent the spread of damage. ViralState:: Whether the PMem module is currently viral. One of: * 0: Not Viral * 1: Viral - The viral policies of the PMem module have switched the persistent memory to read-only mode due to the host operating system software detecting an uncorrectable error situation and indicating a viral state. AitDramEnabled:: If the PMem module AIT DRAM is enabled. One of: * 0: Disabled - The device will suffer performance degradation if the AIT DRAM becomes disabled. * 1: Enabled BootStatus:: The initialization status of the PMem module as reported by the firmware in the boot status register. One or more of: * DDRT/SMBUS Status Unknown - PMem module DDRT and SMBUS interface status unknown. * BSR Unknown - The boot status register cannot be read. * Success - No errors were reported during initialization. * Reboot Required - PMem module's internal state requires a platform power cycle. + *The following statuses indicate the status of DDRT and SMBUS interfaces. Access to PMem module will fail when both interfaces are not available.* * DDRT Not Ready - DDRT interface not ready. * SMBUS Not Ready - SMBUS interface not ready. + *The following statuses indicate that the media is not functional and, therefore, access to user data and operations that require use of the media will fail.* * Media Not Ready - The firmware did not complete media training. * Media Error - The firmware detected an error during media training. * Media Disabled - The firmware disabled the media due to a critical issue. + *The following statuses indicate that communication with the firmware is not functional.* * Mailbox Not Ready - Mailbox interface not ready. BootStatusRegister:: The raw hex value of the PMem module Boot Status Register of the PMem module LatchSystemShutdownState:: Status of the latch. Specifies whether the PMem module will latch the SMART Last Shutdown Status and SMART Dirty Shutdown Count. * 0: Disabled - This is the default. * 1: Enabled PreviousPowerCycleLatchSystemShutdownState:: The status of the latch during the previous power cycle. * 0: Disabled - This is the default. * 1: Enabled ExtendedAdrEnabled:: Specifies whether extended ADR flow is enabled in the FW. * 0: Disabled * 1: Enabled PpcExtendedAdrEnabled:: Specifies whether extended ADR flow was enabled in the FW during the last power cycle. * 0: Disabled * 1: Enabled ErrorInjectionEnabled:: Error injection status. * 0: Disabled - This is the default. * 1: Enabled MediaTemperatureInjectionEnabled:: Media temperature injection status. * 0: Disabled - This is the default. * 1: Enabled SoftwareTriggersEnabled:: Software trigger status. * 0: Disabled - This is the default. * 1: At least one software trigger enabled. SoftwareTriggersEnabledDetails:: Comma separated list of software triggers currently enabled. One or more of: * None * Package Sparing * Fatal Error * Percentage Remaining * Dirty Shutdown PoisonErrorInjectionsCounter:: This counter is incremented each time the set poison error is successfully executed. PoisonErrorClearCounter:: This counter is incremented each time the clear poison error is successfully executed. MediaTemperatureInjectionsCounter:: This counter is incremented each time the media temperature is injected. SoftwareTriggersCounter:: This counter is incremented each time a software trigger is enabled. MaxMediaTemperature:: The highest die temperature reported in degrees Celsius. This value is persistent through Power Loss as well as not effected by Overwrite PMem module or Media Temperature Error Injection. MaxControllerTemperature:: The highest controller temperature reported in degrees Celsius. This value is persistent through Power Loss as well as not effected by Overwrite PMem module. MixedSKU:: One or more PMem modules in the system have different SKUs. One of: * 0: False * 1: True - In this case, the host software operates in a read-only mode and does not allow changes to the PMem modules and their associated capacity. FIPSModeStatus:: The FIPS mode status of the PMem module. One of: * Non-FIPS mode: The default mode from Intel manufacturing * Non-FIPS mode, but will transition to FIPS mode on next boot: The PM regions will be inaccessible in this state. Some command restrictions apply. * FIPS mode, one-time initialization not done: The PM regions will be inaccessible in this state. Some command restrictions apply. * FIPS mode, one-time initialization done: After successful Initialize FIPS Mode firmware command invocation ipmctl-03.00.00.0485/Documentation/ipmctl/Discovery/ipmctl-show-memory-resources.txt000066400000000000000000000144311440615110200302130ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-memory-resources(1) =============================== endif::manpage[] NAME ---- ipmctl-show-memory-resources - Shows PMem module and DDR memory allocation SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -memoryresources -- DESCRIPTION ----------- Shows PMem module and DDR memory allocation information for this platform. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] -u (B|MB|MiB|GB|GiB|TB| TiB):: -units (B|MB|MiB|GB|GiB|TB| TiB):: Changes the units that capacities are displayed in for this command. One of: bytes (B), megabytes (MB), mebibytes (MiB), gigabytes (GB), gibibytes (GiB), terabytes (TB) or tebibytes (TiB). EXAMPLES -------- Shows the PMem module and DDR memory allocation. [listing] -- ipmctl show -memoryresources -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software. RETURN DATA ----------- Returns a table containing the attributes listed below. NOTE: Capacities from unmanageable PMem modules are not included in the following aggregated totals. Volatile DDR Capacity:: Total DDR capacity that is used as volatile memory. Volatile PMem module Capacity:: Total PMem module capacity that is used as volatile memory. Total Volatile Capacity:: Total DDR and PMem module capacity that is used as volatile memory. AppDirect PMem module Capacity:: Total PMem module capacity used as persistent memory. Total AppDirect Capacity:: Total DDR and PMem module capacity used as persistent memory. Cache DDR Capacity:: Total DDR capacity used as a cache for PMem modules. Total Cache Capacity:: Total DDR capacity used as a cache for PMem modules. Inaccessible DDR Capacity:: Total DDR capacity that is inaccessible. Inaccessible PMem module Capacity:: Total PMem module capacity that is inaccessible due to any of: * Platform configuration prevents accessing this capacity. For example, MemoryCapacity is configured but MemoryMode is not enabled by platform FW (current Memory Mode is 1LM). * Capacity is inaccessible because it is not mapped into the System Physical Address space (SPA). This is usually due to platform firmware memory alignment requirements. * Persistent capacity that is reserved. This capacity is the persistent memory partition capacity (rounded down for alignment) less any App Direct capacity. Reserved capacity typically results from a Memory Allocation Goal request that specified the Reserved property. This capacity is not mapped to System Physical Address space (SPA). * Capacity that is unusable because it has not been configured. * PMem module configured capacity but SKU prevents usage. For example, AppDirectCapacity but PMem module SKU is MemoryMode only. Total Inaccessible Capacity:: Total capacity of DDR and PMem module that is inaccessible. Physical DDR Capacity:: Total physical DDR capacity populated on the platform. Physical PMem module Capacity:: Total physical PMem module capacity populated on the platform. Total Physical Capacity:: Total physical capacity populated on the platform. DETAILS ------- PMem modules are partitioned into Memory and Persistent partitions. Memory partitions are aligned on a 1 GiB boundary by ipmctl with the Persistent partition consuming the remaining capacity. Any capacity that falls outside the Memory and Persistent partitions is InaccessibleCapacity and is not usable. If the PMem module is configured for 100% Memory Mode, then the Memory partition consumes all of the capacity and the Persistent partition has none. The mode determines how the DDR capacities are allocated. In 1LM and AppDirect, all DDR capacity is used as additional volatile memory. In MemoryMode, all DDR capacity that the CPU will support is used as a cache for the PMem module. Platform firmware alignment restrictions may result in some capacity from the Memory and Persistent partitions not mapped to System Physical Address space (SPA). This memory is considered InaccessibleCapacity and is not usable. The definitions and calculations that follow intend to describe how each of the values are determined. *Definitions:* Intel PMem module Current Config:: See the _Intel(R) Optane(TM) Persistent Memory Software-Firmware Interface Specification_, document number 556488, for details. PMem module Partition Info:: PMem module partition information provided by PMem module firmware. See the _Intel(R) Optane(TM) Firmware Interface Specification_, document number 626912, for details. PMem module Physical Capacity (PC):: Total usable capacity reported by PMem module Partition Info PMem module Memory Partition Capacity (MPC):: Volatile capacity reported by PMem module Partition Info PMem module Persistent Partition Capacity (PPC):: Persistent capacity reported by PMem module Partition Info PMem module Volatile Memory Capacity (VMC):: Usable volatile memory capacity as reported by platform FW via the _Intel(R) PMem module Current Config->Volatile Memory Size Mapped into SPA_ field PMem module Persistent Memory Capacity (PMC) :: Usable persistent memory capacity as reported by platform FW via the _Intel(R) PMem module Current Config->Persistent Memory Size Mapped into SPA_ field DDR Total Capacity (DDRTC):: Total amount of memory available for use on the populated DDRs *Calculations:* [listing] -- if (CurrentMode == 1LM) then DDRCacheCapacity = 0 DDRVolatileCapacity = DDRTC else if (CurrentMode == 2LM) then DDRCacheCapacity = DDRTC DDRVolatileCapacity = 0 -- [listing] -- TotalVolatileCapacity = VMC + DDRVolatileCapacity -- [listing] -- InaccessibleCapacity = PC - PMC if (CurrentMode != 1LM) then InaccessibleCapacity -= VMC (rounded down for alignment) -- ipmctl-03.00.00.0485/Documentation/ipmctl/Discovery/ipmctl-show-socket.txt000066400000000000000000000047301440615110200261640ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-socket(1) ===================== endif::manpage[] NAME ---- ipmctl-show-socket - Shows basic information about the physical processors SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -socket [TARGETS] -- DESCRIPTION ----------- Shows basic information about the physical processors in the host server. OPTIONS ------- -a:: -all:: Shows all attributes. NOTE: The all and display options are exclusive and may not be used together. -d (attributes):: -display (attributes):: Filters the returned attributes by explicitly specifying a comma separated list of any of the attributes defined in the Return Data section. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] -u (B|MB|MiB|GB|GiB|TB| TiB):: -units (B|MB|MiB|GB|GiB|TB| TiB):: Changes the units that capacities are displayed in for this command. One of: bytes (B), megabytes (MB), mebibytes (MiB), gigabytes (GB), gibibytes (GiB), terabytes (TB) or tebibytes (TiB). TARGETS ------- -socket [SocketIDs]:: Restricts output to the PMem modules installed on specific sockets by supplying the socket target and one or more comma separated socket identifiers. The default is to display all sockets. EXAMPLES -------- Displays information about all the processors. [listing] -- ipmctl show -socket -- Lists all properties for socket 1. [listing] -- ipmctl show -socket 1 -- Retrieves specific properties for each processor. [listing] -- ipmctl show -d MappedMemoryLimit -socket -- RETURN DATA ----------- Displays a table with the attributes listed below for each physical processor installed in the host server. SocketID:: (Default) The processor socket identifier. MappedMemoryLimit:: (Default) The maximum amount of memory that is allowed to be mapped into the system physical address space for this processor based on its SKU. TotalMappedMemory:: (Default) The total amount of memory that is currently mapped into the system physical address space for this processor. ipmctl-03.00.00.0485/Documentation/ipmctl/Discovery/ipmctl-show-system-capabilities.txt000066400000000000000000000157551440615110200306600ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-system-capabilities(1) ================================== endif::manpage[] NAME ---- ipmctl-show-system-capabilities - Shows the platform supported PMem module capabilities. SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -system -capabilities -- DESCRIPTION ----------- Shows the capabilities of the current platform as determined by platform firmware (BIOS) and this application. OPTIONS ------- -a:: -all:: Shows all attributes. NOTE: The all and display options are exclusive and may not be used together. -d (attributes):: -display (attributes):: Filters the returned attributes by explicitly specifying a comma separated list of any of the attributes defined in the Return Data section. NOTE: The all and display options are exclusive and may not be used together. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] -u (B|MB|MiB|GB|GiB|TB| TiB):: -units (B|MB|MiB|GB|GiB|TB| TiB):: Changes the units that capacities are displayed in for this command. One of: bytes (B), megabytes (MB), mebibytes (MiB), gigabytes (GB), gibibytes (GiB), terabytes (TB) or tebibytes (TiB). EXAMPLES -------- Displays the supported PMem module capabilities [listing] -- ipmctl show -system -capabilities -- RETURN DATA ----------- The default behavior is to return the default attributes listed below; the options can be used to expand or restrict the output. PlatformConfigSupported:: (Default) Whether the platform level configuration of PMem modules can be modified with the host software. One of: * 0: Changes must be made in the BIOS. * 1: The command <> is supported. Alignment:: (Default) Capacity alignment requirement for all memory types as reported by the BIOS. AllowedVolatileMode:: (Default) The volatile mode allowed as determined by BIOS setup. One of: * 1LM: One-level volatile mode. All configured PMem module resources on the platform assigned for memory usage are not in use. * 2LM: Two-level volatile mode/Memory Mode. PMem module resources configured as memory act as system memory. Any DDR on the platform will act as a cache working in conjunction with the PMem modules. * Unknown: The allowed volatile mode cannot be determined. CurrentVolatileMode:: (Default) The current volatile mode. One of: * 1LM: One-level volatile mode. All configured PMem module resources on the platform assigned for memory usage are not in use. * 2LM: Two-level volatile mode/Memory Mode. PMem module resources configured as memory act as system memory. Any DDR on the platform will act as a cache working in conjunction with the PMem modules. * Unknown: The current volatile mode cannot be determined. AllowedAppDirectMode:: (Default) The App Direct mode allowed as determined by BIOS setup. One of: * Disabled: App Direct support is currently disabled by the BIOS. * App Direct: App Direct support is currently enabled by the BIOS. * Unknown: The current App Direct support cannot be determined. ModesSupported:: A list of PMem module modes supported by the BIOS. Refer to the command <> to determine the modes supported by the individual PMem modules. At least one of: * 1LM: One-level volatile mode. All configured PMem module resources on the platform assigned for memory usage are not in use. * 2LM: Two-level volatile mode/Memory Mode. PMem module resources configured as memory act as system memory. Any DDR on the platform will act as a cache working in conjunction with the PMem modules. * App Direct: PMem module resources are under direct load/store control of the application. DDR is unaffected. * Unknown: The current mode cannot be determined. SupportedAppDirectSettings:: The BIOS supported list of App Direct interleave settings in the format: * x[Way] - [(IMCSize) iMC x (ChannelSize) Channel] followed by the input format for the command <>: (ByOne|(IMCSize)_(ChannelSize)). RecommendedAppDirectSettings:: The BIOS recommended list of App Direct interleave settings in the format: * x[Way] - [(IMCSize) iMC x (ChannelSize) Channel] followed by the input format for the command <>: (ByOne|(IMCSize)_(ChannelSize)). MinNamespaceSize:: The minimum allowed namespace size as reported by the driver. AppDirectMirrorSupported:: If the BIOS supports App Direct mirroring. One of: * 0: Not supported * 1: Supported DimmSpareSupported:: If the BIOS supports PMem module sparing. One of: * 0: Not supported * 1: Supported AppDirectMigrationSupported:: If the BIOS supports App Direct migration. One of: * 0: Not supported * 1: Supported RenameNamespaceSupported:: If the host software supports renaming a namespace. One of: * 0: Not supported * 1: Supported GrowAppDirectNamespaceSupported:: If the host software supports increasing the capacity of an App Direct namespace. One of: * 0: Not supported * 1: Supported ShrinkAppDirectNamespaceSupported:: If the host software supports decreasing the capacity of an App Direct namespace. One of: * 0: Not supported * 1: Supported InitiateScrubSupported:: If the platform and host software support initiating an address range scrub on the PMem modules in the system. One of: * 0: Not supported * 1: Supported AdrSupported:: Whether the platform supports asynchronous DRAM refresh (ADR). One of: * 0: Not supported. If ADR is not supported, App Direct data integrity cannot be assured during system interruptions. * 1: Supported EraseDeviceDataSupported:: Whether Erase Device Data is supported. * 0: Not supported * 1: Supported EnableDeviceSecuritySupported:: Whether Enable Device Security is supported. * 0: Not supported * 1: Supported DisableDeviceSecuritySupported:: Whether Change Device Security property Lockstate = Disabled is supported. * 0: Not supported * 1: Supported UnlockDeviceSecuritySupported:: Whether Change Device Security property Lockstate = Unlocked is supported. * 0: Not supported * 1: Supported FreezeDeviceSecuritySupported:: Whether Change Device Security property Lockstate = Frozen is supported. * 0: Not supported * 1: Supported ChangeDevicePassphraseSupported:: Whether Change Device Passphrase is supported. * 0: Not supported * 1: Supported ChangeMasterPassphraseSupported:: Whether Change Master Passphrase is supported. * 0: Not supported * 1: Supported MasterEraseDeviceDataSupported:: Whether Master Erase Device Data is supported. * 0: Not supported * 1: Supported ipmctl-03.00.00.0485/Documentation/ipmctl/Discovery/ipmctl-show-topology.txt000066400000000000000000000061241440615110200265470ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-topology(1) ======================= endif::manpage[] NAME ---- ipmctl-show-topology - Shows the topology of the memory installed SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -topology [TARGETS] -- DESCRIPTION ----------- Shows the topology of the memory installed in the host server. Use the command ipmctl-show-dimm to view more detailed information about a PMem module. OPTIONS ------- -a:: -all:: Shows all attributes. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] -u (B|MB|MiB|GB|GiB|TB| TiB):: -units (B|MB|MiB|GB|GiB|TB| TiB):: Changes the units that capacities are displayed in for this command. One of: bytes (B), megabytes (MB), mebibytes (MiB), gigabytes (GB), gibibytes (GiB), terabytes (TB) or tebibytes (TiB). TARGETS ------- -dimm [DimmIDs]:: Restricts output to specific PMem modules by optionally supplying the PMem module target and one or more comma separated PMem module identifiers. The default is to display all PMem modules. -socket [SocketIDs]:: Restricts output to the PMem modules installed on specific sockets by supplying the socket target and one or more comma separated socket identifiers. The default is to display all sockets. NOTE: If ACPI PMTT table is not present, then DDR memory will not be displayed in the filtered socket list. EXAMPLES -------- Displays the system memory topology. [listing] -- ipmctl show -topology -- RETURN DATA ----------- Displays a table with the attributes listed below for each memory module installed in the host server. MemoryType:: (Default) The DIMM type. One of: * Unknown * DDR4 * DDR5 * Logical Non-volatile Device Capacity:: (Default) The raw capacity of the PMem module as reported in the SMBIOS Type 17 table. DimmID:: (Default) The DIMM identifier. For DRAM DIMMs, the DimmID is "N/A". PhysicalID:: (Default) The PMem module physical identifier (i.e., SMBIOS Type 17 handle). DeviceLocator:: (Default) The string that identifies the physically labeled socket or board position where the PMem module is located. SocketID:: The processor socket identifier (i.e., NUMA node) where the PMem module is installed. DieID:: The processor die identifier where the PMem module is installed. MemControllerID:: The associated memory controller identifier. ChannelID:: The associated channel. For DRAM DIMMs, the channel identifier is "N/A". ChannelPos:: The PMem module position in the channel. NodeControllerID:: The node controller identifier. BankLabel:: The string that identifies the physically labeled bank where the PMem module is located. ipmctl-03.00.00.0485/Documentation/ipmctl/Instrumentation/000077500000000000000000000000001440615110200231175ustar00rootroot00000000000000ipmctl-03.00.00.0485/Documentation/ipmctl/Instrumentation/ipmctl-set-sensor.txt000066400000000000000000000062431440615110200272550ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-set-sensor(1) ==================== endif::manpage[] NAME ---- ipmctl-set-sensor - Sets the threshold or enabled state for PMem modules sensors SYNOPSIS -------- [listing] -- ipmctl set [OPTIONS] -sensor (SENSORS) [TARGETS] AlarmThreshold=(temperature) AlarmEnabled=(0|1) -- DESCRIPTION ----------- Changes the alarm threshold or enabled state for one or more PMem modules sensors. Use the command Show Sensor to view the current settings. OPTIONS ------- -f:: -force:: Changing the sensor settings is a potentially destructive operation which requires confirmation from the user for each PMem module. This option suppresses the confirmation. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] SENSORS ------- MediaTemperature:: The PMem module media temperature in Celsius. ControllerTemperature:: The PMem module controller temperature in Celsius. PercentageRemaining:: Remaining PMem module’s life as a percentage value of factory expected life span. TARGETS ------- -dimm [DimmIDs]:: Update specified sensors on specific PMem modules by optionally supplying the PMem module target and one or more comma separated PMem module identifiers. The default is to update the specified sensors for all manageable PMem modules. PROPERTIES ---------- AlarmThreshold:: The threshold value at which an alarm for the respective sensor will be triggered. The upper (for temperatures) or lower (for percentage remaining) alarm threshold of the sensor. Temperatures may be specified to a precision of 1 degree Celsius. + .Allowed AlarmThreshold |=== |Sensor |Allowed AlarmThreshold values |Units |MediaTemperature |0-85 |Celsius |ControllerTemperature |0-102 |Celsius |PercentageRemaining |1-99 |% |=== AlarmEnabled:: Enable or disable the alarm threshold, where applicable. One of: * "0": Disable * "1": Enable EXAMPLES -------- Changes the media temperature alarm threshold to 51C on the specified PMem module and enables the alarm. [listing] -- ipmctl set -sensor MediaTemperature -dimm 0x0001 AlarmThreshold=51 AlarmEnabled=1 -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software. RETURN DATA ----------- For each PMem module, the CLI will indicate the status of the operation. If a failure occurs when modifying multiple PMem modules, the process will exit and not continue modifying the remaining PMem modules. SAMPLE OUTPUT ------------- [listing] -- Modify (Sensor) settings on DIMM (DimmID): Success -- [listing] -- Modify (Sensor) settings on DIMM (DimmID): Error (Code) - (Description) -- ipmctl-03.00.00.0485/Documentation/ipmctl/Instrumentation/ipmctl-show-performance.txt000066400000000000000000000060311440615110200304250ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-performance(1) ========================== endif::manpage[] NAME ---- ipmctl-show-performance - Shows performance metrics for one or more PMem modules SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -performance [METRICS] [TARGETS] -- DESCRIPTION ----------- Shows performance metrics for one or more PMem modules. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] METRICS ------- Restricts output to a specific performance metric by supplying the metric name. See RETURN DATA for more information. One of: * MediaReads * MediaWrites * ReadRequests * WriteRequests * TotalMediaReads * TotalMediaWrites * TotalReadRequests * TotalWriteRequests The default is to display all performance metrics. TARGETS ------- -dimm [DimmIDs]:: Restricts output to the performance metrics for specific PMem module by supplying one or more comma separated PMem module identifiers. The default is to display performance metrics for all manageable PMem module. EXAMPLES -------- Shows all performance metrics for all PMem modules in the server. [listing] -- ipmctl show -dimm -performance -- Shows the number of 64 byte reads since last AC cycle for all PMem modules in the server. [listing] -- ipmctl show -dimm -performance MediaReads -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software. RETURN DATA ----------- This command displays a table of the specified metrics for each specified PMem module. Applying a specific PMem module target limits the rows in the table. Applying a specific metric name target limits the columns in the table. DimmID:: The PMem module identifier MediaReads:: Number of 64 byte reads from media on the PMem module since last AC cycle. MediaWrites:: Number of 64 byte writes to media on the PMem module since last AC cycle. ReadRequests:: Number of DDRT read transactions the PMem module has serviced since last AC cycle. WriteRequests:: Number of DDRT write transactions the PMem module has serviced since last AC cycle. TotalMediaReads:: Number of 64 byte reads from media on the PMem module over its lifetime. TotalMediaWrites:: Number of 64 byte writes to media on the PMem module over its lifetime. TotalReadRequest:: Number of DDRT read transactions the PMem module has serviced over its lifetime. TotalWriteRequest:: Number of DDRT write transactions the PMem module has serviced over its lifetime. ipmctl-03.00.00.0485/Documentation/ipmctl/Instrumentation/ipmctl-show-sensor.txt000066400000000000000000000127301440615110200274400ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-sensor(1) ===================== endif::manpage[] NAME ---- ipmctl-show-sensor - Shows sensor data for one or more PMem modules SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -sensor [SENSORS] [TARGETS] -- DESCRIPTION ----------- Shows sensor data for one or more PMem modules. This data includes PMem module health, temperatures, percentage remaining, up-time and more. OPTIONS ------- -a:: -all:: Shows all attributes. NOTE: The all and display options are exclusive and may not be used together. -d (attributes):: -display (attributes):: Filters the returned attributes by explicitly specifying a comma separated list of any of the attributes defined in the Return Data section. NOTE: The all and display options are exclusive and may not be used together. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] SENSORS ------- Health:: (Default) The current PMem module health as reported in the SMART log. Attributes include: * DimmID * Type * CurrentValue MediaTemperature:: (Default) The current PMem module media temperature in Celsius. Attributes include: * DimmID * Type * CurrentValue * AlarmThreshold * AlarmEnabled * ThrottlingStopThreshold * ThrottlingStartThreshold * ShutdownThreshold * MaxTemperature ControllerTemperature:: (Default) The current PMem module controller temperature in Celsius. Attributes include: * DimmID * Type * CurrentValue * AlarmThreshold * AlarmEnabled * ThrottlingStopThreshold * ThrottlingStartThreshold * ShutdownThreshold * MaxTemperature PercentageRemaining:: (Default) Remaining PMem module’s life as a percentage value of factory expected life span. Attributes include: * DimmID * Type * CurrentValue * AlarmThreshold * AlarmEnabled LatchedDirtyShutdownCount:: (Default) The number of shutdowns without notification over the lifetime of the PMem module. Attributes include: * DimmID * Type * CurrentValue UnlatchedDirtyShutdownCount:: (Default) The number of shutdowns without notification over the lifetime of the PMem module. This counter is the same as LatchedDirtyShutdownCount except it will always be incremented on a dirty shutdown, even if Latch System Shutdown Status was not enabled. Attributes include: * DimmID * Type * CurrentValue PowerOnTime:: (Default) The total power-on time over the lifetime of the PMem module. Attributes include: * DimmID * Type * CurrentValue UpTime:: (Default) The total power-on time since the last power cycle of the PMem module. Attributes include: * DimmID * Type * CurrentValue PowerCycles:: (Default) The number of power cycles over the lifetime of the PMem module. Attributes include: * DimmID * Type * CurrentValue FwErrorCount:: (Default) The total number of firmware error log entries. Attributes include: * DimmID * Type * CurrentValue TARGETS ------- -dimm [DimmIDs]:: Restricts output to the sensors on specific PMem modules by supplying the DIMM target and one or more comma separated PMem module identifiers. The default is to display sensors for all manageable PMem modules. EXAMPLES -------- Gets all sensor information for all PMem modules. [listing] -- ipmctl show -sensor -- Shows the media temperature sensor for the specified PMem module. [listing] -- ipmctl show -sensor MediaTemperature -dimm 0x0001 -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software. RETURN DATA ----------- This command displays a table with a row for each sensor on each PMem module. Applying a specific PMem module or sensor type target limits the number of rows. Applying options can be used to expand or restrict the output. NOTE: Supported thresholds for a given sensor are listed as a part of the return data when using the -all or -display flag. Only the alarm threshold is settable. DimmID:: (Default) The PMem module identifier Type:: (Default) The sensor type. Refer to the sensor table above. CurrentValue:: (Default) The current reading followed by the units of measurement (e.g., 57 °C or 25%) AlarmThreshold:: The threshold value at which an alarm for the respective sensor will be triggered. AlarmEnabled:: Current state of the alarm threshold, where applicable. One of: * 0: Disabled * 1: Enabled * N/A ThrottlingStopThreshold:: The threshold value at which firmware will stop throttling to reduce overall thermals and keep the PMem module within safe operating temperatures. ThrottlingStartThreshold:: The threshold value at which firmware will start throttling to reduce overall thermals and keep the PMem module within safe operating temperatures. ShutdownThreshold:: The threshold value at which device shutdown will occur. MaxTemperature:: The highest temperature reported in degrees Celsius for a given media or controller sensor. This value is persistent through Power Loss and is read-only. ipmctl-03.00.00.0485/Documentation/ipmctl/Memory_Subsystem_Provisioning/000077500000000000000000000000001440615110200260105ustar00rootroot00000000000000ipmctl-03.00.00.0485/Documentation/ipmctl/Memory_Subsystem_Provisioning/ipmctl-create-goal.txt000066400000000000000000000253201440615110200322240ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-create-goal(1) ===================== endif::manpage[] NAME ---- ipmctl-create-goal - Creates a memory allocation goal on one or more PMem modules SYNOPSIS -------- [listing] -- ipmctl create [OPTIONS] -goal [TARGETS] [PROPERTIES] -- DESCRIPTION ----------- Creates a memory allocation goal on one or more for the BIOS to read on the next reboot in order to map the PMem module capacity into the system address space. Persistent memory can then be utilized by creating a namespace. NOTE: The capacity values presented by this command are a target goal or request to platform firmware. The actual capacity values are subject to change due to rounding and alignment requirements. If the goal request is invalid or not possible it may be rejected by platform firmware. NOTE: Deleting the PCD can be used as a way to prepare individual PMem modules for provisioning. See the delete -pcd command. include::../ipmctl-command-data-loss-warning.txt[] include::../ipmctl-set-goal-config-note.txt[] OPTIONS ------- -f:: -force:: Reconfiguring PMem modules is a destructive operation which requires confirmation from the user. This option suppresses the confirmation. This option can also be used to recover/override corrupted Platform Configuration Data (PCD). The force flag will also suppress the security enabled warning as well as all other warning prompts. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". The "nvmxml" format implies the "-force" flag. endif::os_build[] -u (B|MB|MiB|GB|GiB|TB| TiB):: -units (B|MB|MiB|GB|GiB|TB| TiB):: Changes the units that capacities are displayed in for this command. One of: bytes (B), megabytes (MB), mebibytes (MiB), gigabytes (GB), gibibytes (GiB), terabytes (TB) or tebibytes (TiB). TARGETS ------- -dimm [DimmIDs]:: Creates a memory allocation goal on specific PMem modules by optionally supplying one or more comma separated PMem module identifiers. This list must meet one of the following criteria: . Target all PMem modules on a given socket. * For example, all PMem modules located within a single socket may be specified together using the dimm target. . Target all unconfigured PMem modules * For example, when PMem modules have had their PCD deleted, then these PMem modules may all be specified together using the dimm target. . Target PMem modules for 100% MemoryMode with all unspecified PMem modules configured for MemoryMode only * For example, when existing PMem modules are 100% MM, then any newly added PMem modules may all be specified together using the dimm target and configured as 100% MM to match the existing configuration. . Target PMem modules for App Direct Not Interleaved with all unspecified PMem modules configured for App Direct Not Interleaved only * For example, when existing PMem modules are 100% App Direct Not Interleaved, then any newly added PMem modules may all be specified together using the dimm target and configured as 100% App Direct Not Interleaved to match the existing configuration. + NOTE: If the dimm target is not specified, the default is to configure all manageable PMem modules on all sockets. -socket [SocketIDs]:: Loads the memory allocation goal onto all manageable PMem modules on specific sockets by supplying the socket target and one or more comma separated socket identifiers. The default is to load the memory allocation goal onto all manageable PMem modules on all sockets. PROPERTIES ---------- MemoryMode:: Percentage of the total capacity to use in Memory Mode [underline]#(0-100)#. Default = 0. PersistentMemoryType:: If MemoryMode is not 100%, the type of persistent memory to create. * "AppDirect": (Default) Create App Direct capacity utilizing hardware interleaving across the requested PMem modules if applicable given the specified target. * "AppDirectNotInterleaved": Create App Direct capacity that is not interleaved any other PMem modules. NamespaceLabelVersion:: The version of the namespace label storage area (LSA) index block * "1.2": (Default) Defined in UEFI 2.7a - sections 13.19 * "1.1": Legacy 1.1 namespace label support Reserved:: Reserve a percentage [underline]#(0-100)# of the requested PMem module App Direct capacity that will not be mapped into the system physical address space and will be presented as Reserved Capacity with <> and <> commands. EXAMPLES -------- Configures all the PMem module capacity in Memory Mode. [listing] -- ipmctl create -goal MemoryMode=100 -- Configures all the PMem module capacity as App Direct. [listing] -- ipmctl create -goal PersistentMemoryType=AppDirect -- Configures the capacity on each PMem module with 20% of the capacity in Memory Mode and the remaining as App Direct capacity that does not use hardware interleaving. [listing] -- ipmctl create -goal MemoryMode=20 PersistentMemoryType=AppDirectNotInterleaved -- Configures the PMem module capacity across the entire system with 25% of the capacity in Memory Mode, 25% reserved and the remaining 50% as App Direct. Configures the PMem module capacity across the entire system with 25% of the capacity in Memory Mode and the remaining 75% as App Direct. [listing] -- ipmctl create -goal MemoryMode=25 PersistentMemoryType=AppDirect Reserved=25 -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software and must all have the same SKU. * SKU based maximum total mapped memory is enforced. See section <>. * If no PMem modules are specified, all PMem modules in a non-functional or unmanageable state will not be included in the goal's creation. * If the goal request is for an interleaved set, PMem modules in population violation will not be included in the goal's creation. * Existing memory allocation goals that have not been applied and any namespaces associated with the requested PMem modules must be deleted before running this command. * Goal requests may not be applied by platform firmware (BIOS) if the PMem module is in security enabled, locked state. NOTE: It is recommended to disable security prior to reboot if requesting a new goal. NOTE: A goal request may be initiated even if a target PMem module is in security state enabled, but care must be taken to ensure the PMem module is in either unlocked or disabled security state prior to the platform firmware (BIOS) provisioning flow following a reboot. In addition, a warning will be presented to the user: 'WARNING: Goal will not be applied unless security is disabled prior to platform firmware (BIOS) provisioning!' * Changing the memory configuration is a destructive operation which results in loss of data stored in the persistent memory region. Therefore, data should be backed up to other storage before executing this command. Targets may be limited to individual PMem modules or sockets, but all PMem modules on affected sockets must be configured when the command finishes. If the selected targets make this impossible, the command will be rejected. Refer to <> for a list of BIOS supported modes. * Some requests are dependent on BIOS and/or platform configuration. For details, see the _Intel(R) Optane(TM) DC Persistent Memory Software Memory Allocation Rules for Windows, Linux and UEFI_, document number 564194. For example: - Provisioning PMem modules for Memory Mode while BIOS is configured for 1LM only will result in unused capacity. - Provisioning PMem modules for Memory Mode while not all iMCs have at least one PMem module will result in unused capacity. * PMem modules in population violation can be targeted for goal creation only if PersistentMemoryType=AppDirectNotInterleaved and no volatile memory is requested (100% ADx1). RETURN DATA ----------- Minor adjustments (up to 10%) in the requested capacities are sometimes necessary to align properly according to the platform rules. There are also some situations that require additional confirmation from the user because they may result in a non- optimal configuration (i.e., reduced performance). These are described below.: *The requested goal may result in a non-optimal configuration due to the population of PMem modules in the system.* + Memory Mode capacity requested but the population of DRAM DIMMs and PMem modules in the system may result in reduced performance (i.e., the ratio of DRAM and PMem modules is not balanced, DRAM and PMem modules are not on the same channel or not all the same size). *The requested goal may result in a non-optimal configuration due to the population of PMem modules in the system.* + App Direct capacity requested but the population of PMem modules in the system may result in reduced performance (i.e., PMem modules are not the same size or populated asymmetrically across the socket). *The requested goal will result in App Direct capacity which is not supported by the host software.* + App Direct capacity requested but App Direct is not supported by the currently installed host software. *The requested goal will result in Memory Mode capacity that is unusable with the currently selected platform BIOS volatile mode.* + Memory Mode capacity requested by the platform BIOS is currently set to 1LM Mode. *The requested goal was adjusted more than 10% to find a valid configuration.* + > 10% adjustment from the requested goal *The amount of mapped memory was limited based on the SKU resulting in un-mapped capacity.* + Mapped memory was limited based on the CPU SKU. Therefore, before making any changes to the configuration, a prompt is displayed showing the memory allocation goals that will be created on each PMem module as documented in the command Section <>, along with any additional confirmation messages. The force option can be used to override this confirmation and proceed directly with creating the goals. [listing] -- The following configuration will be applied: SocketID DimmID MemorySize AppDirect1Size AppDirect2Size (Refer to the command Section <>) [Additional Confirmation Messages (see above)] Do you want to continue? -- ipmctl-03.00.00.0485/Documentation/ipmctl/Memory_Subsystem_Provisioning/ipmctl-delete-goal.txt000066400000000000000000000061161440615110200322250ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-delete-goal(1) ===================== endif::manpage[] NAME ---- ipmctl-delete-goal - Deletes the memory allocation goal from PMem modules SYNOPSIS -------- [listing] -- ipmctl delete [OPTIONS] -goal [TARGETS] -- DESCRIPTION ----------- Deletes the memory allocation goal from one or more PMem modules. This command only deletes a memory allocation goal request that has not yet been processed by platform firmware (BIOS). If the PMem module target is used and the specified PMem modules do not include all PMem modules that are part of a memory allocation goal request the result will be PMem modules (those not included) that have a broken request which will be rejected by platform firmware (BIOS) upon the next reboot. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -lpmb:: Used to specify large transport payload size for the current invocation of ipmctl. -spmb:: Used to specify small transport payload size for the current invocation of ipmctl. NOTE: The -lpmb and -spmb options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] TARGETS ------- -dimm [DimmIDs]:: Deletes the memory allocation goal from specific PMem modules by optionally supplying one or more comma separated PMem module identifiers. The default is to delete the memory allocation goals from all manageable PMem modules. -socket [SocketIDs]:: Deletes the memory allocation goal from the PMem modules on specific sockets by supplying the socket target and one or more comma separated socket identifiers. The default is to delete the memory allocation goals from manageable PMem modules on all sockets. EXAMPLES -------- Deletes the memory allocation goal from all PMem modules on all sockets. [listing] -- ipmctl delete -goal -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software and unlocked if security is enabled. * Given socket and all specified PMem modules must contain a memory allocation goal. RETURN DATA ----------- For each PMem module, the CLI will indicate the status of the operation. If a failure occurs when deleting the memory allocation goals from multiple PMem modules, the process will output a failure message for those PMem modules that failed and a success message for those that succeeded. SAMPLE OUTPUT ------------- [listing] -- Delete memory allocation goal from PMem module (DimmID): Success -- [listing] -- Delete memory allocation goal from PMem module (DimmID): Error (Code) - (Description) -- ipmctl-03.00.00.0485/Documentation/ipmctl/Memory_Subsystem_Provisioning/ipmctl-dump-goal.txt000066400000000000000000000067621440615110200317370ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-dump-goal(1) =================== endif::manpage[] NAME ---- ipmctl-dump-goal - Stores the current configured memory allocation settings to a file SYNOPSIS -------- [listing] -- ipmctl dump [OPTIONS] -destination (path) -system -config -- DESCRIPTION ----------- Store the currently configured memory allocation settings for all PMem modules in the system to a file in order to replicate the configuration elsewhere. Apply the stored memory allocation settings using the command Section <>. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -lpmb:: Used to specify large transport payload size for the current invocation of ipmctl. -spmb:: Used to specify small transport payload size for the current invocation of ipmctl. NOTE: The -lpmb and -spmb options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] EXAMPLES -------- Stores the memory allocation settings from all the PMem modules into the file "config.txt". [listing] -- ipmctl dump -destination config.txt -system -config -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * Only memory allocation settings for manageable PMem modules that have been successfully applied by the BIOS are stored in the file. Unconfigured PMem modules are not included, nor are memory allocation goals that have not been applied. RETURN DATA ----------- The CLI will indicate the overall status of the operation when complete. If a failure occurs when dumping the memory allocation from multiple PMem modules, the process will stop and the output file will be removed. The output file is formatted as an ASCII file with one row per PMem module containing the following comma separated values. SocketID:: Identifier for the socket the PMem module is associated with. DimmHandle:: PMem module device handle. Capacity:: Total capacity of the PMem module in GiB. MemorySize:: Capacity of the PMem module allocated as Memory Mode in GiB. AppDirect1Size:: Capacity of the PMem module allocated for the first App Direct interleave set in GiB. AppDirect1Format:: Bit mask representing the interleave format of the first App Direct interleave set. AppDirect1Index:: Unique index of the first App Direct interleave set. AppDirect2Size:: Capacity of the PMem module allocated for the second App Direct interleave set in GiB. AppDirect2Format:: Bit mask representing the interleave format of the second App Direct interleave set. AppDirect2Index:: Unique index of the second App Direct interleave set. SAMPLE OUTPUT ------------- [listing] -- Successfully dumped system configuration to file: config.csv -- config.csv contents: [listing] -- #SocketID,DimmHandle,Capacity,MemorySize,AppDirect1Size,AppDirect 1Format,AppDirect1Index,AppDirect2Size,AppDirect2Format,AppDirect2Index 1,4385,64,64,0,0,0,0,0,0 1,4401,64,64,0,0,0,0,0,0 1,4417,64,64,0,0,0,0,0,0 1,4433,64,64,0,0,0,0,0,0 1,4449,64,64,0,0,0,0,0,0 -- ipmctl-03.00.00.0485/Documentation/ipmctl/Memory_Subsystem_Provisioning/ipmctl-load-goal.txt000066400000000000000000000135611440615110200317040ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-load-goal(1) =================== endif::manpage[] NAME ---- ipmctl-load-goal - Creates a memory allocation goal request from a file SYNOPSIS -------- [listing] -- ipmctl load [OPTIONS] -source (path) -goal [TARGETS] -- DESCRIPTION ----------- Creates a memory allocation goal request from a file onto one or more PMem modules. NOTE: Deleting the PCD can be used as a way to prepare individual PMem modules for provisioning. See the delete -pcd command. include::../ipmctl-command-data-loss-warning.txt[] include::../ipmctl-set-goal-config-note.txt[] OPTIONS ------- -f:: -force:: Reconfiguring PMem modules is a destructive operation which requires confirmation from the user. This option suppresses the confirmation. The force flag will also suppress the security enabled warning as well as all other warning prompts. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -lpmb:: Used to specify large transport payload size for the current invocation of ipmctl. -spmb:: Used to specify small transport payload size for the current invocation of ipmctl. NOTE: The -lpmb and -spmb options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". The "nvmxml" format implies the "-force" flag. endif::os_build[] -u (B|MB|MiB|GB|GiB|TB| TiB):: -units (B|MB|MiB|GB|GiB|TB| TiB):: Changes the units that capacities are displayed in for this command. One of: bytes (B), megabytes (MB), mebibytes (MiB), gigabytes (GB), gibibytes (GiB), terabytes (TB) or tebibytes (TiB). TARGETS ------- -dimm [DimmIDs]:: Loads the memory allocation goal onto specific PMem modules by supplying one or more comma separated PMem module identifiers. This list must include all unconfigured PMem modules on the affected sockets. The default is to load the memory allocation goal onto all manageable PMem modules. -socket [SocketIDs]:: Loads the memory allocation goal onto all manageable PMem modules on specific sockets by supplying the socket target and one or more comma separated socket identifiers. The default is to load the memory allocation goal onto all manageable PMem modules on all sockets. EXAMPLES -------- Loads the configuration settings stored in "config.txt" onto all the PMem modules in the system as a memory allocation goal to be applied by the BIOS on the next reboot. [listing] -- ipmctl load -source config.txt -goal -- Loads the configuration settings stored in "config.txt" onto a specified set of PMem modules as a memory allocation goal to be applied by the BIOS on the next reboot. [listing] -- ipmctl load -source config.txt -goal -dimm 1,2,3 -- Loads the configuration settings stored in "config.txt" onto all manageable PMem modules on sockets 1 and 2 as a memory allocation goal to be applied by the BIOS on the next reboot. [listing] -- ipmctl load -source config.txt -goal -socket 1,2 -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software and must all have the same SKU. * SKU based maximum total mapped memory is enforced. See section <>. * Existing memory allocation goals that have not been applied and any namespaces associated with the requested PMem modules must be deleted before running this command. * Goal requests may not be applied by platform firmware (BIOS) if the PMem module is in security enabled, locked state. NOTE: It is recommended to disable security prior to reboot if requesting a new goal. NOTE: A goal request may be initiated even if a target PMem module is in security state enabled, but care must be taken to ensure the PMem module is in either unlocked or disabled security state prior to the platform firmware (BIOS) provisioning flow following a reboot. In addition, a warning will be presented to the user: 'WARNING: Goal will not be applied unless security is disabled prior to platform firmware (BIOS) provisioning!' * Changing the memory configuration is a destructive operation which results in loss of data stored in the persistent memory region. Therefore, data should be backed up to other storage before executing this command. Targets may be limited to individual PMem modules or sockets, but all PMem modules on affected sockets must be configured when the command finishes. If the selected targets make this impossible, the command will be rejected. Refer to <> for a list of BIOS supported modes. * Some requests are dependent on BIOS and/or platform configuration. For details, see the _Intel(R) Optane(TM) DC Persistent Memory Software Memory Allocation Rules Specification for Windows, Linux and UEFI_, document number 564194. For example: - Provisioning PMem modules for Memory Mode while BIOS is configured for 1LM only will result in unused capacity. - Provisioning PMem modules for Memory Mode while not all iMCs have at least one PMem module will result in unused capacity. RETURN DATA ----------- If successful, the CLI will display the memory allocation goal stored on each PMem module as documented in the command Section <>. If a failure occurs, an error code and message will be displayed. If a failure occurs when configuring multiple PMem modules, the process will exit and remove the memory allocation goal from any PMem modules that succeeded prior to the failure. ipmctl-set-dimm-lockstate.txt000066400000000000000000000076641440615110200335030ustar00rootroot00000000000000ipmctl-03.00.00.0485/Documentation/ipmctl/Memory_Subsystem_Provisioning// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-set-dimm-lockstate(1) ============================ endif::manpage[] NAME ---- ipmctl-set-dimm-lockstate - Changes the PMem module security lock state SYNOPSIS -------- [listing] -- ipmctl set [OPTIONS] -dimm [TARGETS] Lockstate=(Unlocked|Disabled|Frozen) Passphrase=(string) -- DESCRIPTION ----------- Changes the data-at-rest security lock state for the persistent memory on one or more PMem modules. ifdef::os_build[] NOTE: This command is subject to OS Vendor (OSV) support. It will return "Not Supported." An exception is if the PMem module is Unlocked Security State, then transitioning to Disabled is permitted. endif::os_build[] OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] -source (path):: File path to a local file containing the new passphrase (1-32 characters). TARGETS ------- -dimm [DimmIDs]:: Changes the lock state of a specific PMem modules by supplying one or more comma separated PMem module identifiers. However, this is not recommended as it may put the system in an undesirable state. The default is to modify all manageable PMem modules. PROPERTIES ---------- LockState:: The desired lock state. * "Disabled": Removes the passphrase on an PMem module to disable security. Permitted only when LockState is Unlocked. * "Unlocked": Unlocks the persistent memory on a locked PMem module. * "Frozen": Prevents further lock state changes to the PMem module until the next reboot. Passphrase:: The current passphrase (1-32 characters). For better passphrase protection, specify an empty string (e.g., Passphrase="") to be prompted for the current passphrase or to use a file containing the passphrases with the source option. EXAMPLES -------- Unlocks device 0x0001. [listing] -- ipmctl set -dimm 0x0001 LockState=Unlocked Passphrase="" -- Unlocks device 0x0001 by supplying the passphrase in the file "mypassphrase.file". In this example, the format of the file would be: #ascii + Passphrase=myPassphrase [listing] -- ipmctl set -source myfile.file -dimm 0x0001 LockState=Unlocked Passphrase="" -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software, have security enabled, not be in the "Frozen" or "Exceeded" lock states, and not executing a long operation (ARS, Overwrite, FWUpdate). ifdef::os_build[] The command is subject to OS Vendor (OSV) support. If OSV does not provide support, the command may return "Not Supported." An exception is if the PMem module is Unlocked (via UEFI or OSV tool), then transitioning to Disabled is possible regardless of OSV support. endif::os_build[] RETURN DATA ----------- If an empty string is provided for the passphrase property and the source option is not included, the user will be prompted (once for all PMem modules) to enter the current passphrase. The passphrase characters are hidden. Current passphrase: **** For each PMem module, the CLI will indicate the status of the security state change. If a failure occurs when changing multiple PMem modules, the process will exit and not continue updating the remaining PMem modules. SAMPLE OUTPUT ------------- [listing] -- Unlock PMem module (DimmID): Success Unlock PMem module (DimmID): Error (Code) - (Description) Remove passphrase from PMem module (DimmID): Success Remove passphrase from PMem module (DimmID): Error (Code) - (Description) -- ipmctl-set-dimm-passphrase.txt000066400000000000000000000124501440615110200336500ustar00rootroot00000000000000ipmctl-03.00.00.0485/Documentation/ipmctl/Memory_Subsystem_Provisioning// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-set-dimm-passphrase(1) ============================= endif::manpage[] NAME ---- ifndef::os_build[] ipmctl-set-dimm-passphrase - Changes the security passphrase on PMem module endif::os_build[] ifdef::os_build[] ipmctl-set-dimm-passphrase - Changes the security passphrase on PMem module on supported OS endif::os_build[] SYNOPSIS -------- [listing] -- ipmctl set [OPTIONS] -dimm [TARGETS] Passphrase=(string) NewPassphrase=(string) ConfirmPassphrase=(string) -- DESCRIPTION ----------- Changes the security passphrase on one or more PMem modules. ifdef::os_build[] NOTE: This command is subject to OS Vendor (OSV) support. It will return "Not Supported." endif::os_build[] OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -master:: Changes the master passphrase. Valid only if master passphrase is enabled on specified PMem modules (see MasterPassphraseEnabled attribute in section <>). -default:: Use this option when the current master passphrase is set to the default value. Valid only if used along with the '-master' option. May not be combined with the Passphrase property. -source (path):: File path to a local file containing the new passphrase (1-32 characters). NOTE: The file does not need to contain the ConfirmPassphrase property. TARGETS ------- -dimm [DimmIDs]:: Changes the passphrase on specific PMem modules by supplying one or more comma separated PMem module identifiers. However, this is not recommended as it may put the system in an undesirable state. The default is to change the passphrase on all manageable PMem modules. PROPERTIES ---------- Passphrase:: The current passphrase (1-32 characters). For better passphrase protection, specify an empty string (e.g., Passphrase="") to be prompted for the current passphrase or to use a file containing the passphrases with the source option. NewPassphrase:: The new passphrase (1-32 characters). For better passphrase protection, specify an empty string (e.g., NewPassphrase="") to be prompted for the passphrase or to use a file containing the passphrase with the source option. ConfirmPassphrase:: Confirmation of the new passphrase (1-32 character and must match NewPassphrase). For better passphrase protection, specify an empty string (e.g., ConfirmPassphrase="") to be prompted for the passphrase or to use a file containing the passphrase with the source option. EXAMPLES -------- Changes the passphrase from mypassphrase to mynewpassphrase on all PMem modules. [listing] -- ipmctl set -dimm Passphrase=mypassphrase NewPassphrase=mynewpassphrase ConfirmPassphrase=mynewpassphrase -- Changes the passphrase on all PMem modules by having the CLI prompt for the current and new passphrases. [listing] -- ipmctl set -dimm Passphrase="" NewPassphrase="" ConfirmPassphrase="" -- Changes the passphrase on all PMem modules by supplying the current and new passphrases from the specified file. In this example, the format of the file would be: #ascii + Passphrase=myOldPassphrase + NewPassphrase=myNewPassphrase [listing] -- ipmctl set -source passphrase.file -dimm Passphrase="" NewPassphrase="" ConfirmPassphrase="" -- Changes the default master passphrase to masterpassphrase on all PMem modules. [listing] -- ipmctl set -master -default -dimm NewPassphrase=masterpassphrase ConfirmPassphrase= masterpassphrase -- Changes the master passphrase from masterpassphrase to newmasterpassphrase on all PMem modules. [listing] -- ipmctl set -master -dimm Passphrase=masterpassphrase NewPassphrase=newmasterpassphrase ConfirmPassphrase=newmasterpassphrase -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem module must be manageable by the host software. * To change the passphrase, the specified PMem module must be "Unlocked" and not be in the "Frozen" or "Exceeded" lock states. * To change the master passphrase, the specified PMem module must be in the "Disabled" lock state and not be in "MP Exceeded" lock state. ifdef::os_build[] Command is subject to OS Vendor (OSV) support. If OSV does not provide support, command will return "Not Supported." endif::os_build[] RETURN DATA ----------- If empty strings are provided for the passphrase properties and the source option is not included, the user will be prompted (once for all PMem module) to enter the current passphrase, then again for the new passphrase and then again to confirm the new passphrase as described below. The passphrase characters are hidden. Current passphrase: \**** For each PMem module, the CLI will indicate the status of the passphrase change operation. If a failure occurs when updating the passphrase on multiple PMem modules, the process will exit and not continue updating the remaining PMem modules. SAMPLE OUTPUT ------------- [listing] -- Change passphrase on DIMM (DimmID): Success -- [listing] -- Change passphrase on DIMM (DimmID): Error (Code)-(Description) -- ipmctl-03.00.00.0485/Documentation/ipmctl/Memory_Subsystem_Provisioning/ipmctl-set-dimm-security.txt000066400000000000000000000076301440615110200334310ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-set-dimm-security(1) =========================== endif::manpage[] NAME ---- ifndef::os_build[] ipmctl-set-dimm-security - Enables data-at-rest security on PMem module endif::os_build[] ifdef::os_build[] ipmctl-set-dimm-security - Enables data-at-rest security on PMem module on supported OS endif::os_build[] SYNOPSIS -------- [listing] -- ipmctl set [OPTIONS] -dimm [TARGETS] NewPassphrase=(string) ConfirmPassphrase=(string) -- DESCRIPTION ----------- Enable data-at-rest security for the persistent memory on one or more PMem modules by setting a passphrase. ifdef::os_build[] NOTE: This command is subject to OS Vendor (OSV) support. It will return "Not Supported." endif::os_build[] OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -source (path):: File path to a local file containing the new passphrase (1-32 characters). NOTE: The file does not need to contain the ConfirmPassphrase property. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] TARGETS ------- -dimm [DimmIDs]:: Set the passphrase on specific PMem modules by supplying one or more comma separated PMem module identifiers. However, this is not recommended as it may put the system in an undesirable state. The default is to set the passphrase on all manageable PMem modules. PROPERTIES ---------- NewPassphrase:: The new passphrase (1-32 characters). For better passphrase protection, specify an empty string (e.g., NewPassphrase="") to be prompted for the passphrase or to use a file containing the passphrase with the source option. ConfirmPassphrase:: Confirmation of the new passphrase (1-32 character and must match NewPassphrase). For better passphrase protection, specify an empty string (e.g., ConfirmPassphrase="") to be prompted for the passphrase or to use a file containing the passphrase with the source option. EXAMPLES -------- Set a passphrase on PMem module 0x0001. [listing] -- ipmctl set -dimm 0x0001 NewPassphrase=123 ConfirmPassphrase=123 -- Sets a passphrase on PMem module 0x0001 by supplying the passphrase in the file mypassphrase.file. In this example, the format of the file would be: #ascii + NewPassphrase=myNewPassphrase [listing] -- ipmctl set -source mypassphrase.file -dimm 0x0001 NewPassphrase="" ConfirmPassphrase="" -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem module must have security disabled and be manageable by the host software. * There must not be any goal creation pending. ifdef::os_build[] Command is subject to OS Vendor (OSV) support. If OSV does not provide support, command will return "Not Supported." endif::os_build[] RETURN DATA ----------- If empty strings are provided for the passphrase properties and the source option is not included, the user will be prompted (once for all PMem modules) to enter the new passphrase and then again to confirm the new passphrase as described below. The passphrase characters will be hidden. New passphrase: \\**** + Confirm new passphrase: \**** For each PMem module, the CLI will indicate the status of the set passphrase operation. If a failure occurs when setting the passphrase on multiple PMem modules, the process will exit and not continue updating the remaining PMem modules. SAMPLE OUTPUT ------------ [listing] -- Set passphrase on PMem module (DimmID): Success -- [listing] -- Set passphrase on PMem module (DimmID): Error (Code) - (Description) -- ipmctl-03.00.00.0485/Documentation/ipmctl/Memory_Subsystem_Provisioning/ipmctl-show-goal.txt000066400000000000000000000115401440615110200317400ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-goal(1) =================== endif::manpage[] NAME ---- ipmctl-show-goal - Shows the memory allocation goal on one or more PMem modules SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -goal [TARGETS] [PROPERTIES] -- DESCRIPTION ----------- Shows the memory allocation goal on one or more PMem modules. Once the goal is successfully applied by the platform firmware (BIOS), it is no longer displayed. Use the command <> to view the system-wide memory resources or the command <> for detailed persistent memory information. OPTIONS ------- -a:: -all:: Shows all attributes. NOTE: The all and display options are exclusive and may not be used together. -d (attributes):: -display (attributes):: Filters the returned attributes by explicitly specifying a comma separated list of any of the attributes defined in the Return Data section. NOTE: The all and display options are exclusive and may not be used together. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] -u (B|MB|MiB|GB|GiB|TB| TiB):: -units (B|MB|MiB|GB|GiB|TB| TiB):: Changes the units that capacities are displayed in for this command. One of: bytes (B), megabytes (MB), mebibytes (MiB), gigabytes (GB), gibibytes (GiB), terabytes (TB) or tebibytes (TiB). TARGETS ------- -dimm [DimmIDs]:: Restricts output to specific PMem modules by supplying one or more comma separated PMem module identifiers. The default is to display all manageable PMem modules with memory allocation goals. -socket [SocketIDs]:: Restricts output to the PMem modules on specific sockets by supplying the socket target and one or more comma separated socket identifiers. The default is to display all manageable PMem modules on all sockets with memory allocation goals. EXAMPLES -------- Shows the default memory allocation goal attributes for each PMem module. [listing] -- ipmctl show -goal -- Shows all the memory allocation goal attributes for the PMem modules on socket 1. [listing] -- ipmctl show -a -goal -socket 1 -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software. RETURN DATA ----------- The default behavior is to display a table with the default attributes for each PMem module; applying options changes the output to a more detailed format. SocketID:: (Default) The processor socket identifier where the PMem module is installed. DimmID:: (Default) The PMem module identifier MemorySize:: (Default) The PMem module capacity that will be configured in Memory Mode. AppDirect1Size:: (Default) The PMem module capacity that will be configured as the first App Direct interleave set if applicable. AppDirect1Index:: Unique identifier of the first App Direct interleave set. * N/A: If no App Direct interleave set * Numeric value if App Direct interleave set is present. AppDirect1Settings:: The settings for the first App Direct interleave set in the format: x(Way) [- (Size) iMC] [x (Size) Channel] AppDirect2Size:: (Default) The PMem module capacity that will be configured as the second App Direct interleave set if applicable. AppDirect2Index:: Unique identifier of the second App Direct interleave set. * N/A: If no App Direct interleave set * Numeric value if App Direct interleave set is present. AppDirect2Settings:: The settings for the second App Direct interleave set in the format: x(Way) [- (Size) iMC] [x (Size) Channel] Status:: The status of the memory allocation goal. One of: * Unknown: The status cannot be determined. * New: A reboot is required for the memory allocation goal to be processed by the BIOS. * Failed - Bad request: The BIOS failed to process the memory allocation goal because it was invalid. * Failed - Not enough resources: There were not enough resources for the BIOS to process the memory allocation goal. * Failed - Firmware error: The BIOS failed to process the memory allocation goal due to a firmware error. * Failed - Unknown: The BIOS failed to process the memory allocation goal due to an unknown error. SAMPLE OUTPUT ------------- If a new memory allocation goal has been created, a prompt to reboot will be presented. [listing] -- A reboot is required to process new memory allocation goals. -- ipmctl-03.00.00.0485/Documentation/ipmctl/Persistent_Memory_Provisioning/000077500000000000000000000000001440615110200261525ustar00rootroot00000000000000ipmctl-03.00.00.0485/Documentation/ipmctl/Persistent_Memory_Provisioning/ipmctl-create-namespace.txt000066400000000000000000000105651440615110200334050ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-create-namespace(1) ========================== endif::manpage[] NAME ---- ipmctl-create-namespace - Creates a namespace from a persistent memory region SYNOPSIS -------- [listing] -- ipmctl create [OPTIONS] -namespace -region (RegionID) [PROPERTIES] -- DESCRIPTION ----------- Creates a new namespace from a persistent memory region of PMem module capacity. OPTIONS ------- -f:: -force:: If the resulting namespace capacity does not align properly, a prompt will be displayed to confirm the rounded-up namespace capacity to the next aligned size. This option suppresses the confirmation and proceeds with rounding up the namespace capacity as necessary. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -lpmb:: Used to specify large transport payload size for the current invocation of ipmctl. -spmb:: Used to specify small transport payload size for the current invocation of ipmctl. NOTE: The -lpmb and -spmb options are mutually exclusive and may not be used together. -u (B|MB|MiB|GB|GiB|TB| TiB):: -units (B|MB|MiB|GB|GiB|TB| TiB):: Changes the units that capacities are displayed in for this command. One of: bytes (B), megabytes (MB), mebibytes (MiB), gigabytes (GB), gibibytes (GiB), terabytes (TB) or tebibytes (TiB). TARGETS ------- -region (RegionIDs):: The region identifier on which to create the namespace PROPERTIES ---------- Capacity:: The size of the namespace. Unless otherwise specified by the units option, the capacity is measured in GiB. The default is to use the maximum available capacity on the specified region. Minimum capacity is 1 GiB * (number of PMem modules in target region). For example: * 6 GiB is minimum size for a x6 interleave set. * 1 GiB is minimum size for a x1 non-interleave set. Name:: Optional user specified namespace name to more easily identify the namespace. Up to a maximum of 63 characters. Mode:: The mode for the namespace after creation. One of: * "None": (Default) Raw namespace without any address abstraction. * "Sector": Powerfail block write atomicity is guaranteed via a Block Translation Table (BTT) EXAMPLES -------- Creates an App Direct memory namespace on region 0x01 using default settings. [listing] -- ipmctl create -namespace -region 0x01 -- Creates a 32 GB App Direct namespace on region 0x01. [listing] -- ipmctl create -units GB -namespace -region 0x01 Capacity=32 -- Creates a 32 GB App Direct sector mode namespace on region 0x01. [listing] -- ipmctl create -units GB -namespace -region 0x01 Capacity=32 mode=Sector -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software. * The underlying PMem modules must be unlocked and the region HealthState must be "Healthy." Use the command <> to retrieve namespace limitations as reported by the driver. NOTE: For App Direct namespaces to be compatible across operating systems, it is recommended that a single App Direct namespace consumes the entire capacity (AppDirectNamespaceMaxSize) as reported by the command <>. NOTE: Microsoft* Windows* supports and validates only a single namespace per region configuration. RETURN DATA ----------- If the resulting namespace capacity does not align properly, a prompt will be displayed to confirm the rounded-up namespace capacity to the next aligned size. The force option can be used to override this confirmation and proceed with the rounded up capacity. [listing] -- The requested namespace capacity [capacity] will be rounded up to [capacity] to align properly. Do you want to continue? -- SAMPLE OUTPUT ------------- If the namespace is created successfully, the CLI will display all attributes of the new namespace as documented in the command Section <>. If a failure occurs when creating the namespace, the CLI will display a single error message. [listing] -- Create namespace failed: Error (Code)- (Description) -- ipmctl-03.00.00.0485/Documentation/ipmctl/Persistent_Memory_Provisioning/ipmctl-delete-namespace.txt000066400000000000000000000051741440615110200334040ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-delete-namespace(1) ========================== endif::manpage[] NAME ---- ipmctl-delete-namespace - Deletes one or more existing namespaces SYNOPSIS -------- [listing] -- ipmctl delete [OPTIONS] -namespace [TARGETS] -- DESCRIPTION ----------- Deletes one or more existing namespaces. All data on the deleted namespaces becomes inaccessible. NOTE: Changing the namespace configuration is a destructive operation which may result in loss of data stored in the associated namespace. Therefore, data should be backed up to other storage before executing this command. NOTE: Deleting a namespace removes the logical OS mapping to the persistent memory data, but does not explicitly delete or modify user data found in persistent memory. OPTIONS ------- -f:: -force:: Deleting namespaces is a destructive operation which requires confirmation from the user for each namespace. This option suppresses the confirmation. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -lpmb:: Used to specify large transport payload size for the current invocation of ipmctl. -spmb:: Used to specify small transport payload size for the current invocation of ipmctl. NOTE: The -lpmb and -spmb options are mutually exclusive and may not be used together. TARGETS ------- -namespace [NamespaceIDs]:: Deletes specific namespaces by providing a comma separated list of one or more namespace identifiers. The default is to delete all namespaces. Namespace identifiers are limited to 64 characters. EXAMPLES -------- Deletes namespace 0x02. [listing] -- ipmctl delete -namespace 0x2 -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * Any associated file systems must be un-mounted. * The specified PMem modules must be manageable by the host software and unlocked if security is enabled. RETURN DATA ----------- For each namespace, the CLI will indicate the status of the operation. If a failure occurs when deleting multiple namespaces, the process will continue deleting the remaining namespaces. SAMPLE OUTPUT ------------- [listing] -- Delete namespace (NamespaceID): Success -- [listing] -- Delete namespace (NamespaceID Error (Code) - (Description) Delete namespace (NamespaceID): Success -- ipmctl-03.00.00.0485/Documentation/ipmctl/Persistent_Memory_Provisioning/ipmctl-show-namespace.txt000066400000000000000000000112261440615110200331150ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-namespace(1) ======================== endif::manpage[] NAME ---- ipmctl-show-namespace - Shows information about one or more namespaces SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -namespace [TARGETS] -- DESCRIPTION ----------- Shows information about one or more namespaces. OPTIONS ------- -a:: -all:: Shows all attributes. NOTE: The all and display options are exclusive and may not be used together. -d (attributes):: -display (attributes):: Filters the returned attributes by explicitly specifying a comma separated list of any of the attributes defined in the Return Data section. NOTE: The all and display options are exclusive and may not be used together. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -u (B|MB|MiB|GB|GiB|TB| TiB):: -units (B|MB|MiB|GB|GiB|TB| TiB):: Changes the units that capacities are displayed in for this command. One of: bytes (B), megabytes (MB), mebibytes (MiB), gigabytes (GB), gibibytes (GiB), terabytes (TB) or tebibytes (TiB). TARGETS ------- -namespace [NamespaceIDs]:: Restricts output to specific namespaces by providing a comma separated list of one or more namespace identifiers. The default is to display all namespaces. -region [RegionIDs]:: Restricts output to the namespaces on specific regions by supplying the region target and one or more comma separated region identifiers. The default is to display namespaces on all regions. EXAMPLES -------- Shows the default attributes for the specified namespace. [listing] -- ipmctl show -namespace 0x01 -- Shows all the attributes for all namespaces. [listing] -- ipmctl show -a –namespace -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software and unlocked if security is enabled. Use the command <> to retrieve namespace limitations as reported by the driver. RETURN DATA ----------- The default behavior is to display a table with the default attributes listed below; applying options changes the output to a more detailed format. NamespaceID:: (Default) The identifier of the namespace Capacity:: (Default) The usable capacity of the namespace. This value is the raw namespace capacity, not counting meta data. HealthState:: (Default) The rolled-up health of the underlying PMem modules. One of: * Unknown * Healthy * Warning: Indicates a namespace has a non-critical problem. One of: - Reporting an LBA size that is unsupported by this driver. * Critical: Indicates a namespace has a critical problem. One of: - Inconsistent data structures. - Mismatch between namespace label and associated PM region. - Poison or uncorrectable data in namespace metadata. - Namespace label checksum mismatch. - Mismatch between number of labels reported by namespace and number of labels found (invalid number of labels found). - Namespace parent interleave set not found. - Interleave set or region initialization failure (out of memory). - PMem module missing (serial number of PMem module from the Platform Config Data not found in the PMem module list). - PMem module not configured. - Failure to retrieve AppDirect I/O structures from NFIT (system physical address missing). - Failure to check existence of address abstraction. - Failure to initialize BTT. - Failure to initialize PFN. * Locked: One or more of the of the underlying PMem modules within the namespace are locked. * Unsupported: The namespace type is not supported. Name:: The user specified namespace name up to a maximum of 64 characters RegionID:: The region from which the namespace was created. BlockSize:: The logical size in bytes for compatibility with block read/write operations Mode:: Address abstraction type found on the namespace. One of: * None (raw) * Sector - Powerfail write atomicity is guaranteed via a Block Translation Table (BTT). * fsdax - Supports filesystem-dax (See ndctl-create-namespace) NOTE: If the namespace is disabled, mode may be reported as "None". LabelVersion:: Indicates the Namespace index version that can be set with <>. One of: * 1.1 * 1.2 NamespaceUUID:: UUID of the namespace (raw UUID) ipmctl-03.00.00.0485/Documentation/ipmctl/Persistent_Memory_Provisioning/ipmctl-show-region.txt000066400000000000000000000120761440615110200324500ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-region(1) ===================== endif::manpage[] NAME ---- ipmctl-show-region - Retrieves a list of persistent memory regions. SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -region [TARGETS] -- DESCRIPTION ----------- Retrieves a list of persistent memory regions of PMem module capacity. OPTIONS ------- -a:: -all:: Shows all attributes. NOTE: The all and display options are exclusive and may not be used together. -d (attributes):: -display (attributes):: Filters the returned attributes by explicitly specifying a comma separated list of any of the attributes defined in the Return Data section. NOTE: The all and display options are exclusive and may not be used together. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -lpmb:: Used to specify large transport payload size for the current invocation of ipmctl. -spmb:: Used to specify small transport payload size for the current invocation of ipmctl. NOTE: The -lpmb and -spmb options are mutually exclusive and may not be used together. -nfit:: Used to specify NFIT table as the source instead of PCD (default) for the current invocation of ipmctl. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] -u (B|MB|MiB|GB|GiB|TB| TiB):: -units (B|MB|MiB|GB|GiB|TB| TiB):: Changes the units that capacities are displayed in for this command. One of: bytes (B), megabytes (MB), mebibytes (MiB), gigabytes (GB), gibibytes (GiB), terabytes (TB) or tebibytes (TiB). TARGETS ------- ifndef::os_build[] -region [RegionIDs]:: Restricts output to specific persistent memory regions by providing one or more comma separated region identifiers. The default is to display the persistent memory regions across all manageable PMem modules. endif::os_build[] -socket [SocketIDs]:: Restricts output to the persistent memory regions on specific sockets by supplying the socket target and one or more comma separated socket identifiers. The default is to display all sockets. EXAMPLES -------- Shows all attributes of all persistent memory regions in the server. [listing] -- ipmctl show -a -region -- ifndef::os_build[] Shows all attributes for the specified persistent memory region. [listing] -- ipmctl show -a -region 1 -- endif::os_build[] LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * All the underlying PMem modules should be unlocked to accurately reflect the available capacities. The specified PMem modules must be manageable by the host software. RETURN DATA ----------- The default behavior is to display a table with the default attributes listed below; applying options changes the output to a more detailed format. ifndef::os_build[] RegionID:: (Default) The unique region identifier endif::os_build[] ifdef::os_build[] ISetID:: (Default) The region unique identifier. Also known as interleave set cookie. endif::os_build[] PersistentMemoryType:: (Default) A comma separated list of the underlying types of persistent memory capacity in the region. One or more of: * AppDirect: App Direct capacity interleaved across two or more PMem modules that is fully mapped into the system physical address space. * AppDirectNotInterleaved: App Direct capacity wholly contained on a single PMem modules that is fully mapped into the system physical address space. Capacity:: (Default) Total usable capacity, both allocated and unallocated FreeCapacity:: (Default) Remaining usable capacity SocketID:: (Default) Socket ID to which the region belongs HealthState:: The rolled up health of the underlying PMem modules. One of: * Unknown: The region health cannot be determined. * Healthy: All underlying PMem module persistent memory capacity is available. * Pending: A new memory allocation goal has been created but not applied. Reboot or delete any existing memory allocation goals before creating namespaces on the region. * Error: There is an issue with some or all of the underlying PMem module capacity because the interleave set has failed. One of: - PMem module missing (serial number of PMem module from the Platform Config Data not found in the PMem module list) - PMem module not configured - Failure to retrieve AppDirect I/O structures from NFIT (system physical address missing) - Interleave set or PMem module region initialization failure (out of memory) * Locked: One or more of the of the underlying PMem modules are locked. ifndef::os_build[] ISetID:: The region unique identifier. Also known as interleave set cookie. endif::os_build[] DimmID:: A list of all the PMem modules that are part of this reg. ipmctl-03.00.00.0485/Documentation/ipmctl/Support_and_Maintenance/000077500000000000000000000000001440615110200245145ustar00rootroot00000000000000ipmctl-03.00.00.0485/Documentation/ipmctl/Support_and_Maintenance/ipmctl-dump-support-data.txt000066400000000000000000000057161440615110200321420ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-dump-support-data(1) =========================== endif::manpage[] NAME ---- ipmctl-dump-support-data - Dumps a support snapshot to a file SYNOPSIS -------- [listing] -- ipmctl dump [OPTIONS] -destination (file_prefix) [-dict (filename)] -support -- DESCRIPTION ----------- Creates a support snapshot and dump support data to a file for off-line analysis by support personnel. Support data may include system logs, error logs, trace logs, platform configuration, sensor information and diagnostic results. This command may take significantly longer than other commands to complete, because it combines and executes multiple individual commands. Commands executed: * version * show -memoryresources * show -a -system -capabilities * show -a -topology * start -diagnostic * show -system Commands executed per PMem module: * show -a -dimm * show -a -sensor -dimm * show -pcd -dimm * show -error media -dimm * show -error thermal -dimm * dump -destination -debug OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format of the command execution (the output file content will remain text). One of: "text" (default) or "nvmxml". endif::os_build[] TARGET ------ -destination (filename):: This command creates a text file with a name starting with the given filename option and dumps the platform support information into it. In addition, this command also outputs the debug log information in separate files. Refer to <> for more details. -dict (filename):: Optional file path to the dictionary file. If supplied, the command will create both the binary debug log and a text file with decoded log data with the file prefix specified by -destination. This option is used only to dump the debug log information. EXAMPLES -------- Creates a text file named dumpfile_platform_support_info.txt and stores the platform supported data in that file. Also, dumps the debug log info in the related files that start with the file name dumpfile. Refer to <> for more info on the output files. [listing] -- ipmctl dump -destination filename -dict nlog_dict.1.1.0.0000.txt -support -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software. RETURN DATA ----------- Returns the status of the operation. SAMPLE OUTPUT ------------- On success: [listing] -- Dump support data successfully written to filename_platform_support_info.txt. -- ipmctl-03.00.00.0485/Documentation/ipmctl/Support_and_Maintenance/ipmctl-help.txt000066400000000000000000000017341440615110200275000ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-help(1) ============== endif::manpage[] NAME ---- ipmctl-help - Shows help for the supported commands SYNOPSIS -------- [listing] -- ipmctl help [OPTIONS] -- DESCRIPTION ----------- Shows help for the supported commands. OPTIONS ------- -h:: -help:: Displays help for the command. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] EXAMPLES -------- Lists all supported commands [listing] -- ipmctl help -- RETURN DATA ----------- The default behavior is to display an introduction to CLI followed by a list of the supported commands. To display detailed help for a specific command, use the help option with that specific command. SAMPLE OUTPUT ------------- [listing] -- ipmctl help -- Commands: (command synopsis) (command syntax) \... (command synopsis) (command syntax) ipmctl-03.00.00.0485/Documentation/ipmctl/Support_and_Maintenance/ipmctl-load-dimm.txt000066400000000000000000000123521440615110200304110ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-load-dimm(1) =================== endif::manpage[] NAME ---- ipmctl-load-dimm - Updates the firmware on one or more PMem modules SYNOPSIS -------- [listing] -- ipmctl load [OPTIONS] -source (path) -dimm [TARGETS] -- DESCRIPTION ----------- Updates the firmware on one or more PMem modules. On the next power cycle, the firmware will become active. NOTE: If Address Range Scrub (ARS) is in progress on any target PMem module, an attempt will be made to abort ARS and then proceed with the firmware update. NOTE: A power cycle reboot is required to activate the updated firmware image and is recommended to ensure ARS runs to completion. OPTIONS ------- -x:: -examine:: Verifies the target PMem modules are compatible and ready to receive the firmware image specified in the source option. Returns the firmware image version. -f:: -force:: Downgrading the firmware to an older version is a potentially destructive operation which requires confirmation from the user for each PMem module. This option suppresses the confirmation when attempting to downgrade. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. -lpmb:: Used to specify large transport payload size for the current invocation of ipmctl. -spmb:: Used to specify small transport payload size for the current invocation of ipmctl. NOTE: The -lpmb and -spmb options are mutually exclusive and may not be used together. -source:: Specifies the firmware image binary to upload to the PMem module. -recover:: *--DEPRECATED--* + This flag is no longer necessary to run firmware update on PMem modules where the DDRT link is not trained. These untrained PMem modules are now automatically included when the command is run without the '-recover' option.. However, this flag is still maintained for backwards compatibility. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] TARGETS ------- -dimm [DimmIDs]:: Updates the firmware on specific PMem modules by supplying one or more comma separated PMem module identifiers. However, this is not recommended as it may put the system in an undesirable state. The default is to update all manageable PMem modules. EXAMPLES -------- Updates the firmware on all PMem modules in the system to the image in sourcefile.bin on the next power cycle. [listing] -- ipmctl load -source sourcefile.bin -dimm -- Checks the firmware image in sourcefile.bin and retrieves the version. [listing] -- ipmctl load -examine -source sourcefile.bin -dimm -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software. Firmware version (PN.RN.SV.bbbb) updates are supported as follows: * The product number (PN) cannot be changed. * The revision number (RN) can be upgraded when PN is the same. * The security revision number (SV) can be upgraded when PN.RN is the same. In some configurations it can also be downgraded when PN.RN is the same; use the examine option to determine if the security revision number can be downgraded. * The build number (bbbb) can be upgraded or downgraded. However, if the firmware API version in the firmware image is lower than is supported by the host software and would make the PMem module become unmanageable, the downgrade is not supported. NOTE: Once a firmware image is staged for execution, a power cycle is required before another firmware image of the same type (production or debug) can be staged for execution using this command. RETURN DATA ----------- When the examine option is provided, the firmware image is checked and the version number and firmware type is provided. The firmware will either be valid for the PMem module, a downgrade or invalid meaning it cannot be used for that PMem module. SAMPLE OUTPUT ------------- [listing] (file path): MM.mm.hh.bbbb Load FW on PMem module (DimmID): (Valid|Downgrade) [(with confirmation or the force option)] If the firmware is being downgraded and the force option is not provided, the user will be prompted to confirm the downgrade for each PMem module. Otherwise, for each PMem module, the CLI will indicate the status of the operation. [listing] -- Downgrade firmware on PMem module (DimmID)? (y or [n]) Downgrade firmware on PMem module (DimmID)? (y or [n]) -- If a failure occurs when updating multiple PMem modules, the process will continue attempting to update the remaining PMem modules requested. The firmware will not become active until the next power cycle. Use the command Section <> to view more detailed information about the active and staged firmware. [listing] -- Load FW on PMem module (DimmID): Success, a power cycle is required to activate the FW. -- [listing] -- Load FW on PMem module (DimmID): Error (Code) - (Description) -- ipmctl-03.00.00.0485/Documentation/ipmctl/Support_and_Maintenance/ipmctl-set-dimm.txt000066400000000000000000000041141440615110200302620ustar00rootroot00000000000000// Copyright (c) 2019, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-set-dimm(1) ================== endif::manpage[] NAME ---- ipmctl-set-dimm - Changes the configurable settings on one or more PMem modules SYNOPSIS -------- [listing] -- ipmctl set [OPTIONS] -dimm [TARGETS] [PROPERTIES] -- DESCRIPTION ----------- Changes the configurable settings on one or more PMem modules. OPTIONS ------- -f:: -force:: Changing PMem module settings is a potentially destructive operation which requires confirmation from the user for each PMem module. This option suppresses the confirmation. -h:: -help:: Displays help for the command. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] TARGETS ------- -dimm (DimmIDs):: Modifies specific PMem modules by supplying one or more comma separated PMem module identifiers. However, this is not recommended as it may put the system in an undesirable state. The default is to modify all manageable PMem modules. PROPERTIES ---------- AveragePowerReportingTimeConstant:: The value, in milliseconds, used to determine the time constant for reporting the average power consumption measurements. Can be set to a value between 100 and 12000, by increments of 100. The default value is 1000. EXAMPLES -------- Sets average power reporting time constant multiplier to 1 on all manageable PMem modules. [listing] -- set -dimm AveragePowerReportingTimeConstant=1500 -- LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software. RETURN DATA ----------- For each PMem module, the CLI will indicate the status of the operation. If a failure occurs when modifying multiple PMem modules, the process will exit and not continue modifying the remaining PMem modules. SAMPLE OUTPUT ------------- [listing] -- Modify PMem module (DimmID): Success Modify PMem module (DimmID): Error (Code) - (Description) -- ipmctl-03.00.00.0485/Documentation/ipmctl/Support_and_Maintenance/ipmctl-set-preferences.txt000066400000000000000000000063441440615110200316440ustar00rootroot00000000000000// Copyright (c) 2019, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-set-preferences(1) ========================= endif::manpage[] NAME ---- ipmctl-set-preferences - Modifies one or more user preferences SYNOPSIS -------- [listing] -- ipmctl set [OPTIONS] -preferences [PROPERTIES] -- DESCRIPTION ----------- Modifies one or more user preferences in the PMem module software. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] PROPERTIES ---------- CLI_DEFAULT_DIMM_ID:: The default display of PMem module identifiers. One of: - "UID": Use the DimmUID attribute as defined in the section <>. - "HANDLE": Use the DimmHandle attribute as defined in section <>. This is the default CLI_DEFAULT_SIZE:: The default display of capacities in the CLI. One of: * "AUTO": Automatically choose the best format for each capacity in binary multiples of bytes (i.e., B, MiB, GiB or TiB). This is the default. * "AUTO_10": Automatically choose the best format for each capacity in decimal multiples of bytes (i.e., B, MB, GB or TB). * "B": Displays all capacities in bytes. * "MB": Displays all capacities in megabytes. * "MiB": Displays all capacities in mebibytes. * "GB": Displays all capacities in gigabytes. * "GiB": Displays all capacities in gibibytes. * "TB": Displays all capacities in terabytes. * "TiB": Displays all capacities in tebibytes. APPDIRECT_SETTINGS:: The interleave settings to use when creating App Direct capacity in the format: [underline]#(IMCSize_ChannelSize)#. Must be one of the BIOS supported App Direct settings returned by the command <>. NOTE: ByOne is not a valid setting for this preference. The default is "RECOMMENDED" which uses the BIOS recommended App Direct settings. NOTE: The same interleave settings are used for all the App Direct capacity in the system. Therefore, if any App Direct capacity already exists, this preference cannot be changed. ifdef::os_build[] DBG_LOG_LEVEL:: Whether debug logging is enabled in the PMem module host software. These logs pertain to the operation of the command-line tool only and do not reflect any logging functionality of the PMem module. One of: * "0": Logging is disabled. This is the default. * "1": Log Errors. * "2": Log Warnings, Errors. * "3": Log Informational, Warnings, Errors. * "4": Log Verbose, Informational, Warnings, Errors. endif::os_build[] EXAMPLES -------- Use DimmUID as the default PMem module identifier and display all capacities in bytes. [listing] -- ipmctl set -preferences CLI_DEFAULT_DIMM_ID=UID CLI_DEFAULT_SIZE=B -- RETURN DATA ----------- Returns the status of the operation. SAMPLE OUTPUT ------------- [listing] -- Set (Property)=(Value): Success|Error (Code)-(Description) -- ipmctl-03.00.00.0485/Documentation/ipmctl/Support_and_Maintenance/ipmctl-show-firmware.txt000066400000000000000000000065351440615110200313460ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-firmware(1) ======================= endif::manpage[] NAME ---- ipmctl-show-firmware - Shows detailed information about the firmware SYNOPSIS -------- [listing] -- ipmctl show [OPTIONS] -firmware [TARGETS] -- DESCRIPTION ----------- Shows detailed information about the firmware on one or more PMem modules. OPTIONS ------- -a:: -all:: Shows all attributes. NOTE: The all and display options are exclusive and may not be used together. -d (attributes):: -display (attributes):: Filters the returned attributes by explicitly specifying a comma separated list of any of the attributes defined in the Return Data section. NOTE: The all and display options are exclusive and may not be used together. -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] TARGETS ------- -dimm [DimmIDs]:: Restricts output to the firmware information for specific PMem modules by supplying one or more comma separated PMem module identifiers. The default is to display the firmware information for all manageable PMem modules. EXAMPLES -------- Shows the firmware information for all PMem modules in the server. [listing] ipmctl show -dimm -firmware LIMITATIONS ----------- In order to successfully execute this command: * The caller must have the appropriate privileges. * The specified PMem modules must be manageable by the host software. RETURN DATA ----------- The default behavior is to display a table with the default attributes listed below; the options can be used to expand or restrict the output. DimmID:: (Default) The PMem module identifier ActiveFWVersion:: (Default) The BCD-formatted revision of the active firmware in the format PN.RN.SV.bbbb where: * PN = 2-digit product number * RN = 2-digit revision number * SV = 2-digit security version number * bbbb = 4-digit build version StagedFWVersion:: (Default) The BCD-formatted revision of the firmware staged for execution on the next power cycle in the format PN.RN.SV.bbbb where: * PN = 2-digit product number * RN = 2-digit revision number * SV = 2-digit security version number * bbbb = 4-digit build version StagedFWActivatable:: The state of whether the staged firmware is activatable or not, where: * 0 = Not activatable, reboot is required * 1 = Activatable FWUpdateStatus:: The status of the last firmware update operation. One of: * Unknown * Staged successfully * Update loaded successfully * Update failed to load, fell back to previous firmware FWImageMaxSize:: The maximum size of a firmware image. QuiesceRequired:: The state of whether activating the firmware requires the host to quiesce traffic prior to calling the command. * 0 = No traffic quiesce required prior to activate * 1 = Traffic quiesce required prior to activate ActivationTime:: The estimated activation time, in ms, required for activating the firmware.ipmctl-03.00.00.0485/Documentation/ipmctl/Support_and_Maintenance/ipmctl-show-preferences.txt000066400000000000000000000053171440615110200320300ustar00rootroot00000000000000// Copyright (c) 2019, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-show-preferences(1) ========================== endif::manpage[] NAME ---- ipmctl-show-preferences - Displays a list of the PMem module software user preferences SYNOPSIS -------- [listing] ipmctl show [OPTIONS] -preferences DESCRIPTION ----------- Displays a list of the PMem module software user preferences and their current values. OPTIONS ------- -h:: -help:: Displays help for the command. -ddrt:: Used to specify DDRT as the desired transport protocol for the current invocation of ipmctl. -smbus:: Used to specify SMBUS as the desired transport protocol for the current invocation of ipmctl. NOTE: The -ddrt and -smbus options are mutually exclusive and may not be used together. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] EXAMPLES -------- Displays the current values for all the user preferences. [listing] -- ipmctl show -preferences -- RETURN DATA ----------- CLI_DEFAULT_DIMM_ID:: The default display of PMem module identifiers. One of: * UID: Use the DimmUID attribute as defined in the command <>. * HANDLE: Use the DimmHandle attribute as defined in the command <>. This is the default. CLI_DEFAULT_SIZE:: The default display of capacities in the CLI. One of: * AUTO: Automatically choose the best format for each capacity in binary multiples of bytes (i.e., B, MiB, GiB or TiB). This is the default. * AUTO_10: AUTO_10: Automatically choose the best format for each capacity in decimal multiples of bytes (i.e., B, MB, GB or TB). * B: Displays all capacities in bytes. * MB: Displays all capacities in megabytes. * MiB: Displays all capacities in mebibytes. * GB: Displays all capacities in gigabytes. * GiB: Displays all capacities in gibibytes. * TB: Displays all capacities in terabytes. * TiB: Displays all capacities in tebibytes. APPDIRECT_SETTINGS:: The interleave settings to use when creating App Direct capacity in the format: (IMCSize_ChannelSize). The default is "RECOMMENDED" which uses the BIOS recommended App Direct settings returned by the command <>. ifdef::os_build[] DBG_LOG_LEVEL:: Whether debug logging is enabled in the PMem module host software. These logs pertain to the operation of the command-line tool only and do not reflect any logging functionality of the PMem module. One of: * 0: Logging is disabled. This is the default. * 1: Log Errors. * 2: Log Warnings, Errors. * 3: Log Informational, Warnings, Errors. * 4: Log Verbose, Informational, Warnings, Errors. endif::os_build[] ipmctl-03.00.00.0485/Documentation/ipmctl/Support_and_Maintenance/ipmctl-version.txt000066400000000000000000000025351440615110200302350ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ifdef::manpage[] ipmctl-version(1) ================= endif::manpage[] NAME ---- ipmctl-version - Shows the PMem module host software versions SYNOPSIS -------- [listing] -- ipmctl version [OPTIONS] -- DESCRIPTION ----------- Shows the PMem module host software versions. OPTIONS ------- -h:: -help:: Displays help for the command. ifdef::os_build[] -o (text|nvmxml):: -output (text|nvmxml):: Changes the output format. One of: "text" (default) or "nvmxml". endif::os_build[] EXAMPLES -------- Displays the available version information for the PMem module software components. [listing] -- ipmctl version -- RETURN DATA ----------- By default, returns the following inventory information. Component:: The name of the software component. One of: * [Product Name] Software Version: The PMem module management software version ifndef::os_build[] * [Product Name] Driver Version: The vendor specific PMem module driver version endif::os_build[] Version:: The current version of the software component if found or an error if not. NOTE: If the software version is incompatible, the version will be followed by an error message indicating such. If PMem modules are found with a FIS implementation higher than supported by the SW version, this command will print a warning. ipmctl-03.00.00.0485/Documentation/ipmctl/ipmctl-command-data-loss-warning.txt000066400000000000000000000003161440615110200267110ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause WARNING: *This command may result in data loss. Data should be backed up to other storage before executing this command*. ipmctl-03.00.00.0485/Documentation/ipmctl/ipmctl-set-goal-config-note.txt000066400000000000000000000005441440615110200256670ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause NOTE: Changing a memory allocation goal modifies how the platform firmware maps persistent memory in the System Physical Address space (SPA) which may result in data loss or inaccessible data, but does not explicitly delete or modify user data found in persistent memory. ipmctl-03.00.00.0485/Documentation/ipmctl/ipmctl.txt000066400000000000000000000130161440615110200217460ustar00rootroot00000000000000// Copyright (c) 2018, Intel Corporation. // SPDX-License-Identifier: BSD-3-Clause ipmctl(1) ========= NAME ---- ipmctl - Utility for managing Intel(R) Optane(TM) Persistent Memory Modules (PMem module) SYNOPSIS -------- [listing] -- ipmctl COMMAND [OPTIONS] [TARGETS] [PROPERTIES] -- OPTIONS ------- -h:: --help:: Run ipmctl help command. DESCRIPTION ----------- Utility for managing Intel(R) Optane(TM) PMem modules Supports functionality to: * Discover PMem modules on the platform. * Provision the platform memory configuration. * View and update the firmware on PMem modules. * Configure data-at-rest security on PMem modules. * Monitor PMem module health. * Track performance of PMem modules. * Debug and troubleshoot PMem modules. COMMANDS -------- === DEVICE DISCOVERY Discover PMem modules on the platform. *ipmctl-show-dimm*(1):: Shows information about one or more PMem modules *ipmctl-show-memory-resources*(1):: Shows memory allocation information for this platform *ipmctl-show-socket(1)*:: Shows basic information about the physical processors *ipmctl-show-system-capabilities*(1):: Shows the platform supported PMem module capabilities *ipmctl-show-topology(1)*:: Shows the topology of the memory installed === MEMORY SUBSYSTEM PROVISIONING Provision the platform memory configuration and configure data-at-rest security on PMem modules. ifndef::os_build[] *ipmctl-set-dimm-passphrase*(1):: Changes the security passphrase on PMem module *ipmctl-set-dimm-lockstate*(1):: Changes the PMem module security lock state endif::os_build[] *ipmctl-create-goal(1)*:: Creates a memory allocation goal on one or more PMem module *ipmctl-delete-goal(1)*:: Deletes a memory allocation goal on one or more PMem module *ipmctl-dump-goal(1)*:: Stores the current system configuration in a file ifndef::os_build[] *ipmctl-set-dimm-security(1)*:: Enable data-at-rest security on PMem module *ipmctl-delete-dimm(1)*:: Erases the persistent data on one or more PMem modules endif::os_build[] *ipmctl-load-goal(1)*:: Load a memory allocation goal from a file onto PMem modules *ipmctl-show-goal(1)*:: Shows the memory allocation goal on one or more PMem module *ipmctl-show-region(1)*:: Retrieves a list of persistent memory regions === INSTRUMENTATION Monitor health and track performance of PMem modules. *ipmctl-set-sensor*(1):: Sets the threshold or enabled state for PMem modules sensors *ipmctl-show-performance*(1):: Shows performance metrics for one or more PMem modules *ipmctl-show-sensor*(1):: Shows health statistics for one or more PMem modules === SUPPORT AND MAINTENANCE View and update the firmware on PMem modules. Other support and maintenance commands. *ipmctl-set-preferences*(1):: Modifies one or more user preferences *ipmctl-dump-support*(1):: Dumps a support snapshot to a file *ipmctl-help*(1):: Shows help for the supported commands *ipmctl-set-dimm*(1):: Changes the configurable settings on one or more PMem modules *ipmctl-show-firmware*(1):: Shows detailed information about the firmware *ipmctl-show-preferences*(1):: Displays a list of the PMem module software user preferences *ipmctl-load-dimm*(1):: Updates the firmware on one or more PMem modules. *ipmctl-version*(1):: Shows the PMem module host software versions === DEBUG Debug and troubleshoot PMem modules. *ipmctl-delete-pcd*(1) Clears select partition data from the PCD *ipmctl-dump-debug-log*(1):: Dumps encoded firmware debug logs from PMem module *ipmctl-inject-error*(1):: Injects an error or clears a previously injected error *ipmctl-show-cap*(1):: Shows the current Command Access Policy restrictions. *ipmctl-show-cel*(1):: Shows the current Command Effect Log. *ipmctl-start-diagnostic*(1):: Runs a diagnostic test *ipmctl-show-system*(1):: Shows the system ACPI tables related to the PMem modules *ipmctl-show-error-log*(1):: Shows thermal or media errors on the specified PMem modules *ipmctl-show-pcd*(1):: Shows the platform configuration data for one or more PMem modules *ipmctl-dump-session*(1):: Dumps the active session to a file *ipmctl-load-session*(1):: Loads a session from a file into memory *ipmctl-show-session*(1):: Displays information about an active session *ipmctl-start-session*(1):: Starts a recording or playback session *ipmctl-stop-session*(1):: Stops a recording or playback session SEE ALSO -------- *ipmctl-show-dimm*(1), *ipmctl-show-memory-resources*(1), *ipmctl-show-socket*(1), *ipmctl-show-system-capabilities*(1), *ipmctl-show-topology*(1), ifndef::os_build[] *ipmctl-set-dimm-passphrase*(1), *ipmctl-set-dimm-lockstate*(1), endif::os_build[] *ipmctl-create-goal*(1), *ipmctl-create-goal*(1), *ipmctl-delete-goal*(1), *ipmctl-dump-goal*(1), ifndef::os_build[] *ipmctl-set-dimm-security*(1), *ipmctl-delete-dimm*(1), endif::os_build[] *ipmctl-load-goal*(1), *ipmctl-show-goal*(1), *ipmctl-show-region*(1), *ipmctl-set-sensor*(1), *ipmctl-show-performance*(1), *ipmctl-show-sensor*(1), *ipmctl-set-preferences*(1), *ipmctl-dump-support*(1), *ipmctl-help*(1), *ipmctl-set-dimm*(1), *ipmctl-show-firmware*(1), *ipmctl-show-preferences*(1), *ipmctl-load-dimm*(1), *ipmctl-version*(1), *ipmctl-delete-pcd*(1), *ipmctl-dump-debug-log*(1), *ipmctl-inject-error*(1), *ipmctl-show-cap*(1), *ipmctl-show-cel*(1), *ipmctl-start-diagnostic*(1), *ipmctl-show-system*(1), *ipmctl-show-error-log*(1), *ipmctl-show-pcd*(1) *ipmctl-dump-session*(1) *ipmctl-load-session*(1) *ipmctl-show-session*(1) *ipmctl-start-session*(1) *ipmctl-stop-session*(1) NOTES ----- https://github.com/intel/ipmctl ipmctl-03.00.00.0485/LICENSE000066400000000000000000000030351440615110200146210ustar00rootroot00000000000000/* * Copyright (c) 2018-2021, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Intel Corporation nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ ipmctl-03.00.00.0485/README.md000066400000000000000000000137631440615110200151040ustar00rootroot00000000000000# ipmctl ipmctl is a utility for configuring and managing Intel® Optane™ Persistent Memory modules (PMem). Packaging status It supports functionality to: * Discover PMems on the platform. * Provision the platform memory configuration. * View and update the firmware on PMems. * Configure data-at-rest security on PMems. * Track health and performance of PMems. * Debug and troubleshoot PMems. ipmctl refers to the following interface components: * libipmctl: An Application Programming Interface (API) library for managing PMems. * ipmctl: A Command Line Interface (CLI) application for configuring and managing PMems from the command line. Also, metrics exporter for [Prometheus](https://prometheus.io/docs/introduction/overview/) based on libipmctl was provided. For more details take a look [here](https://github.com/intel/ipmctl-exporter) ## Workarounds ### Slow Firmware Updates When using 02.00.00.x versions of ipmctl software to update or downgrade firmware on Intel® Optane™ PMem 100 Series modules, please use the “-lpmb” CLI option (use DDRT Large Payload transfer). Otherwise the operation may take significantly longer than it normally would. ### Commands Fail on Older Platforms Some platforms that targeted the Gen 100 modules do not generate a ACPI PMTT table which causes ipmctl (version v02.00.00.xxxx) commands to fail. Particularly * create -goal * show -topology * show -memoryresources * show -dimm If these commands are ran with -v option they present a message about failing to get the PMTT table. A corrected version is being developed and will hopefully be available soon. Until that is available the best option is to use ipmctl v01.00.00.xxxx or go through the BIOS menus. ## Releases 01.00.00.xxxx (master_1_0 branch) is for Intel Optane Persistent Memory 100 Series 02.00.00.xxxx (master_2_0 branch) is for Intel Optane Persistent Memory 200 Series (and is backwards compatible with 100 series) 03.00.00.xxxx (master_3_0 branch) is for Intel Optane Persistent Memory 300 Series (and is backwards compatible with both the 100 and 200 series **Note**: Branches may differ fundamentally. Please pay close attention to README.md of the respective branch. ## Packages Some distributions include ipmctl allowing installation via their package manager. For example (on Fedora): > dnf install ipmctl This will update the required dependencies. For systems that cannot reach the Internet use another system to download the following rpms required to install ipmctl and then copy them to the original system (e.g. via thumb drive). > ipmctl.rpm, libipmctl.rpm, libsafec.rpm (only needed for 1.x releases), libndctl, json-c.rpm Run > rpm –ivh *.rpm CentOS and RHEL systems maybe able to use an EPEL package found at: https://src.fedoraproject.org/rpms/ipmctl OpenSUSE and SLES packages can be found at: https://build.opensuse.org/package/show/hardware:nvdimm/ipmctl Ubuntu releases can be found at: https://launchpad.net/ubuntu/+source/ipmctl ### libndctl ipmctl depends on libndctl (ndctl-libs). It can be found here https://github.com/pmem/ndctl if not available as a package. ## Build **Note**: Each branch may require different building procedures. Please follow README.md of the respective branch. ### building latest (03.00.00.xxxx) on Linux 1. clone the ipmctl and edk2 repositories: `git clone -b development https://github.com/intel/ipmctl.git` `git clone https://github.com/tianocore/edk2.git` `cd ipmctl` 2. Give execution permissions to the .sh files: `chmod +x *.sh` 4. Run updateedk.sh, this will copy relevant folders from edk2 into ipmctl `./updateedk.sh` 5. Build the ipmctl rpms specifying the version number to use `./rpmbuild.sh 03.00.00.1234` ### building latest (03.00.00.xxxx) on Windows Install Visual Studio 2017 (or newer). Be sure to install optional component: * Workloads -> Desktop Development with C++ * Individual Components -> Compilers, build tools, and runtimes -> Visual C++ tools for CMake clone the ipmctl project Clone the edk2 repository and copy the directories BaseTools, MdeModulePkg, MdePkg and ShellPkg into the clone of the ipmctl project Open CMakeLists.txt as a CMake project in Visual Studio. See: https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio ### Specific Instructions Reported as Working for 02.00.00.xxxx versions on SUSE for build in home directory Replace homedir with the actual account > git clone https://github.com/intel/ipmctl.git > > zypper in libndctl-devel ruby2.5-rubygem-asciidoctor > > cd ipmctl > > mkdir output > > cd output > > cmake -DRELEASE=ON -DSAFECLIB_SRC_DOWNLOAD_AND_STATIC_LINK=ON -DCMAKE_INSTALL_PREFIX=/home/homedir/ipmctl/ .. > > make all ### Specific Instructions Reported as Working for 02.00.00.xxxx versions on RHEL7.6, CentOS7.6 and Fedora 30. Ipmctl has dependency on libsafec-devel (for 1.x builds only), libndctl-devel and rubygem-asciidoctor * copr/jhli repo has libipmctl and its dependency, libsafec-devel. * cd /etc/yum.repos.d/ > wget https://copr.fedorainfracloud.org/coprs/jhli/ipmctl/repo/epel-7/jhli-ipmctl-epel-7.repo > wget https://copr.fedorainfracloud.org/coprs/jhli/safeclib/repo/epel-7/jhli-safeclib-epel-7.repo > > * This should bring down both libipmctl and its dependency, libsafec-devel and * epel repos has rubygem-asciidoctor * get the epel repos > yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm * Enable extra packages in RHEL for ndctl-devel or any other dependencies * EPEL packages may depend on packages from these repositories: > # subscription-manager repos --enable "rhel-*-optional-rpms" --enable "rhel-*-extras-rpms" --enable "rhel-ha-for-rhel-*-server-rpms" * Install the prerequisite packages > sudo yum install ndctl ndctl-libs ndctl-devel libsafec rubygem-asciidoctor * Either Follow ipmctl make, rpmbuild instructions, or install the ipmctl package ipmctl-03.00.00.0485/install/000077500000000000000000000000001440615110200152615ustar00rootroot00000000000000ipmctl-03.00.00.0485/install/linux/000077500000000000000000000000001440615110200164205ustar00rootroot00000000000000ipmctl-03.00.00.0485/install/linux/libipmctl.pc.in000066400000000000000000000004521440615110200213310ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_FULL_BINDIR@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: libipmctl Description: Manage Intel DC Optane persistent memory modules Version: @BUILDNUM@ Libs: -L${libdir} -lipmctl Cflags: -I${includedir} ipmctl-03.00.00.0485/install/linux/logrotate/000077500000000000000000000000001440615110200204205ustar00rootroot00000000000000ipmctl-03.00.00.0485/install/linux/logrotate/ipmctl.logrotate.conf000066400000000000000000000001031440615110200245500ustar00rootroot00000000000000/var/log/ipmctl/*log { missingok notifempty rotate 4 monthly } ipmctl-03.00.00.0485/install/linux/rel-release/000077500000000000000000000000001440615110200206205ustar00rootroot00000000000000ipmctl-03.00.00.0485/install/linux/rel-release/ipmctl.spec.in000066400000000000000000000104341440615110200233730ustar00rootroot00000000000000%define build_version 99.99.99.9999 %define platform_type purley Name: ipmctl Version: %{build_version} Release: 1%{?dist} Summary: Utility for managing Intel Optane DC persistent memory modules License: BSD-3-Clause URL: https://github.com/intel/ipmctl Source: https://github.com/intel/ipmctl/archive/v%{version}/%{name}-%{version}.tar.gz ExclusiveArch: x86_64 Requires: libipmctl5%{?_isa} = %{version}-%{release} BuildRequires: pkgconfig(libndctl) BuildRequires: cmake %if 0%{?fedora} BuildRequires: python2 %endif %if 0%{?rhel} == 8 BuildRequires: python3 %endif BuildRequires: gcc BuildRequires: gcc-c++ %if 0%{?suse_version} BuildRequires: ruby2.5-rubygem-asciidoctor %else %if 0%{?rhel} == 8 BuildRequires: rubygems %else BuildRequires: asciidoctor %endif %endif Obsoletes: ixpdimm-cli < 01.00.00.3000 %description Utility for managing Intel Optane DC persistent memory modules Supports functionality to: Discover DCPMMs on the platform. Provision the platform memory configuration. View and update the firmware on DCPMMs. Configure data-at-rest security on DCPMMs. Track health and performance of DCPMMs. Debug and troubleshoot DCPMMs. %prep %setup -q -n %{name}-%{version} %package -n libipmctl5 Summary: Library for Intel DCPMM management Obsoletes: ixpdimm_sw < 01.00.00.3000 Obsoletes: libixpdimm-common < 01.00.00.3000 Obsoletes: libixpdimm-core < 01.00.00.3000 Obsoletes: libixpdimm-cli < 01.00.00.3000 Obsoletes: libixpdimm-cim < 01.00.00.3000 Obsoletes: libixpdimm < 01.00.00.3000 Obsoletes: ixpdimm-data < 01.00.00.3000 Obsoletes: libipmctl < 03.00.00.0090 %description -n libipmctl5 An Application Programming Interface (API) library for managing Intel Optane DC persistent memory modules. %package -n libipmctl5-devel Summary: Development packages for libipmctl5 Requires: libipmctl5%{?_isa} = %{version}-%{release} Obsoletes: ixpdimm-devel < 01.00.00.3000 Obsoletes: ixpdimm_sw-devel < 01.00.00.3000 %description -n libipmctl5-devel API for development of Intel Optane DC persistent memory management utilities. %build %cmake -DBUILDNUM=%{version} -DCMAKE_INSTALL_PREFIX=/ \ -DLINUX_PRODUCT_NAME=%{name} \ -DCMAKE_INSTALL_LIBDIR=%{_libdir} \ -DCMAKE_INSTALL_INCLUDEDIR=%{_includedir} \ -DCMAKE_INSTALL_BINDIR=%{_bindir} \ -DCMAKE_INSTALL_DATAROOTDIR=%{_datarootdir} \ -DCMAKE_INSTALL_MANDIR=%{_mandir} \ -DCMAKE_INSTALL_LOCALSTATEDIR=%{_localstatedir} \ -DCMAKE_INSTALL_SYSCONFDIR=%{_sysconfdir} \ -DRELEASE=ON \ -DRPM_BUILD=ON %if 0%{?fedora} > 32 %cmake_build %else %make_build %endif %install %{!?_cmake_version: cd build} %if 0%{?fedora} > 32 %cmake_install %else %make_install %endif %post -n libipmctl5 -p /sbin/ldconfig %postun -n libipmctl5 -p /sbin/ldconfig %files -n ipmctl %{_bindir}/ipmctl %{_mandir}/man1/ipmctl* %files -n libipmctl5 %{_libdir}/libipmctl.so.5* %dir %{_datadir}/doc/ipmctl %doc %{_datadir}/doc/ipmctl/ipmctl_default.conf %doc %{_datadir}/doc/ipmctl/LICENSE %doc %{_datadir}/doc/ipmctl/thirdpartynotice.txt %doc %{_datadir}/doc/ipmctl/edk2_License.txt %dir %{_datadir}/ipmctl %config(noreplace) %{_datadir}/ipmctl/ipmctl.conf %dir %{_localstatedir}/log/ipmctl %config(noreplace) %{_sysconfdir}/logrotate.d/ipmctl %files -n libipmctl5-devel %{_libdir}/libipmctl.so %{_includedir}/nvm_types.h %{_includedir}/nvm_management.h %{_includedir}/export_api.h %{_includedir}/NvmSharedDefs.h %{_libdir}/pkgconfig/libipmctl.pc %changelog * Mon Mar 8 2021 Nolan Hergert - 03.00.00.295-1 - Rev nvm api lib minor version for backwards compatibility changes * Tue Sep 15 2020 Jan Drachal - 03.00.00.227-1 - Update nvm api lib minor version * Wed May 6 2020 Steven Pontsler - 03.00.00.91-1 - change package names to include lib version * Wed Jan 29 2020 Pawel Nass - 03.00.00.36-1 - Change the reference to the share libraries * Thu Oct 10 2019 Amy Wang - 02.00.00.3613-1 - Change the reference to the share libraries * Mon Sep 17 2018 Juston Li - 01.00.00.3262-1 - Clean up spec for fedora review * Wed May 02 2018 Juston Li - 01.00.00.3000-1 - initial spec ipmctl-03.00.00.0485/opensource/000077500000000000000000000000001440615110200157755ustar00rootroot00000000000000ipmctl-03.00.00.0485/opensource/CONTRIBUTING.md000066400000000000000000000023071440615110200202300ustar00rootroot00000000000000# Contributing to ipmctl Thank you for your interest in ipmctl! Please follow the guidelines below when submitting bugs or pull requests. We will do our best to respond in a timely manner. ## Bug reports When filing issues please: 1. Specify the ipmctl version, OS, kernel version, FW version, BIOS version etc. 2. Attach support data - 'ipmctl dump -destination support.txt -support' 3. Attach a debug log reproducing the issue - Enable max debug log verbosity via 'ipmctl set -preferences DBG_LOG_LEVEL=4' - Run the command - Attach the debug log located in /var/log/ipmctl/debug.log (or possibly in the directory you executed ipmctl if you compiled ipmctl) ## Pull requests Pull requests fixing bugs and making small improvements are much appreciated. For larger changes, please file an issue to discuss making larger enhancements. When submitting a pull request: 1. Rebase to the testing branch 2. Sign off on the commit Accepted pull requests will not be directly merged via GitHub. They will be be merged internally for further review before being pushed to the testing branch. Commits may be subject to minor changes. After a validation cycle, it will merged into master under a tagged release. ipmctl-03.00.00.0485/opensource/README.md000066400000000000000000000067401440615110200172630ustar00rootroot00000000000000# ipmctl ipmctl is a utility for configuring and managing Intel Optane DC persistent memory modules (DCPMM). It supports functionality to: * Discover DCPMMs on the platform. * Provision the platform memory configuration. * View and update the firmware on DCPMMs. * Configure data-at-rest security on DCPMMs. * Track health and performance of DCPMMs. * Debug and troubleshoot DCPMMs. ipmctl refers to the following interface components: * libipmctl: An Application Programming Interface (API) library for managing DCPMMs. * ipmctl: A Command Line Interface (CLI) application for configuring and managing DCPMMs from the command line. ## Packages Some distributions include ipmctl allowing installation via their package manager. For example (on Fedora): > dnf install ipmctl This will update the required dependencies. For systems that cannot reach the Internet use another system to download the following rpms required to install ipmctl and then copy them to the original system (e.g. via thumb drive). > ipmctl.rpm, libipmctl.rpm, libsafec.rpm (only needed for 1.x releases), libndctl, json-c.rpm Run > rpm –ivh *.rpm CentOS and RHEL systems maybe able to use an EPEL package found at: https://src.fedoraproject.org/rpms/ipmctl OpenSUSE and SLES packages can be found at: https://build.opensuse.org/package/show/hardware:nvdimm/ipmctl Ubuntu releases can be found at: https://launchpad.net/ubuntu/+source/ipmctl ### libndctl ipmctl depends on libndctl (ndctl-libs). It can be found here https://github.com/pmem/ndctl if not available as a package. ## Build ### Specific Instructions Reported as Working on RHEL7.6, CentOS7.6 and Fedora 30. Ipmctl has dependency on libsafec-devel (for 1.x builds only), libndctl-devel and rubygem-asciidoctor * copr/jhli repo has libipmctl and its dependency, libsafec-devel. * cd /etc/yum.repos.d/ > wget https://copr.fedorainfracloud.org/coprs/jhli/ipmctl/repo/epel-7/jhli-ipmctl-epel-7.repo > wget https://copr.fedorainfracloud.org/coprs/jhli/safeclib/repo/epel-7/jhli-safeclib-epel-7.repo > > * This should bring down both libipmctl and its dependency, libsafec-devel and * epel repos has rubygem-asciidoctor * get the epel repos > yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm * Enable extra packages in RHEL for ndctl-devel or any other dependencies * EPEL packages may depend on packages from these repositories: > # subscription-manager repos --enable "rhel-*-optional-rpms" --enable "rhel-*-extras-rpms" --enable "rhel-ha-for-rhel-*-server-rpms" * Install the prerequisite packages > sudo yum install ndctl ndctl-libs ndctl-devel libsafec rubygem-asciidoctor * Either Follow ipmctl make, rpmbuild instructions, or install the ipmctl package ### Linux The latest stable Linux kernel version available is recommended. libndctl-devel package is required. All other dependencies are widely available. ``` mkdir output && cd output cmake -DRELEASE=ON -DCMAKE_INSTALL_PREFIX=/usr .. make -j all sudo make install ``` build artifacts can be found in output/release To build RPMs: ``` ./rpmbuild.sh xx.xx.xx.xxxx ``` The RPMs will be in output/rpmbuild/RPMS/ ### Windows Install Visual Studio 2017 (or newer). Be sure to install optional component: * Workloads -> Desktop Development with C++ * Individual Components -> Compilers, build tools, and runtimes -> Visual C++ tools for CMake Open as a CMake project. See: https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio ipmctl-03.00.00.0485/opensource/edk2_License.txt000066400000000000000000000051711440615110200210310ustar00rootroot00000000000000Copyright (c) 2019, TianoCore and contributors. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Subject to the terms and conditions of this license, each copyright holder and contributor hereby grants to those receiving rights under this license a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except for failure to satisfy the conditions of this license) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer this software, where such license applies only to those patent claims, already acquired or hereafter acquired, licensable by such copyright holder or contributor that are necessarily infringed by: (a) their Contribution(s) (the licensed copyrights of copyright holders and non-copyrightable additions of contributors, in source or binary form) alone; or (b) combination of their Contribution(s) with the work of authorship to which such Contribution(s) was added by such copyright holder or contributor, if, at the time the Contribution is added, such addition causes such combination to be necessarily infringed. The patent license shall not apply to any other combinations which include the Contribution. Except as expressly stated above, no rights or licenses from any copyright holder or contributor is granted under this license, whether expressly, by implication, estoppel or otherwise. DISCLAIMER THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ipmctl-03.00.00.0485/opensource/opensource_LICENSE000066400000000000000000000030351440615110200212450ustar00rootroot00000000000000/* * Copyright (c) 2018-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Intel Corporation nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ ipmctl-03.00.00.0485/opensource/thirdpartynotice.txt000066400000000000000000000060471440615110200221410ustar00rootroot00000000000000THIRD-PARTY SOFTWARE NOTICES AND INFORMATION This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Intel received such components are set forth below. Intel reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. 1. TianoCore edk2 (BaseTools, MdeModulePkg, MdePkg, ShellPkg), https://github.com/tianocore/edk2 Copyright (c) 2019, TianoCore and contributors. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Subject to the terms and conditions of this license, each copyright holder and contributor hereby grants to those receiving rights under this license a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except for failure to satisfy the conditions of this license) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer this software, where such license applies only to those patent claims, already acquired or hereafter acquired, licensable by such copyright holder or contributor that are necessarily infringed by: (a) their Contribution(s) (the licensed copyrights of copyright holders and non-copyrightable additions of contributors, in source or binary form) alone; or (b) combination of their Contribution(s) with the work of authorship to which such Contribution(s) was added by such copyright holder or contributor, if, at the time the Contribution is added, such addition causes such combination to be necessarily infringed. The patent license shall not apply to any other combinations which include the Contribution. Except as expressly stated above, no rights or licenses from any copyright holder or contributor is granted under this license, whether expressly, by implication, estoppel or otherwise. DISCLAIMER THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ipmctl-03.00.00.0485/patch_OS.sh000077500000000000000000000003721440615110200156540ustar00rootroot00000000000000#!/usr/bin/env bash DIR="./src/os/patches" # Apply patches echo "Applying patches" if [[ -d "$DIR" ]]; then for file in "$DIR"/*.patch do git apply --ignore-space-change --ignore-whitespace --whitespace=nowarn "${file}" done fi ipmctl-03.00.00.0485/rpmbuild.sh000077500000000000000000000021451440615110200157720ustar00rootroot00000000000000#!/bin/bash #apply patches for linux builds ./patch_OS.sh if [ "$#" -le 0 ]; then echo "./rpmbuild.sh xx.xx.xx.xxxx purley | whitley" exit 3 fi BUILDNUM=$1 SOURCEDIR=$PWD # remove all rpmbuild rm -rf $SOURCEDIR/output/rpmbuild # Make directories mkdir -p $SOURCEDIR/output/rpmbuild mkdir -p $SOURCEDIR/output/rpmbuild/BUILDROOT mkdir -p $SOURCEDIR/output/rpmbuild/SOURCES mkdir -p $SOURCEDIR/output/rpmbuild/RPMS mkdir -p $SOURCEDIR/output/rpmbuild/SRPMS mkdir -p $SOURCEDIR/output/rpmbuild/SPECS mkdir -p $SOURCEDIR/output/rpmbuild/ipmctl-$BUILDNUM # Copy spec file cp install/linux/rel-release/ipmctl.spec.in $SOURCEDIR/output/rpmbuild/SPECS/ipmctl.spec # Update the spec file with build version sed -i "s/^%define build_version .*/%define build_version $BUILDNUM/g" $SOURCEDIR/output/rpmbuild/SPECS/ipmctl.spec # Archive the directory tar --exclude-vcs --exclude="*output" --exclude="*.swp*" --transform="s,^.,ipmctl-$BUILDNUM," -zcf $SOURCEDIR/output/rpmbuild/SOURCES/ipmctl-$BUILDNUM.tar.gz . # rpmbuild rpmbuild -ba $SOURCEDIR/output/rpmbuild/SPECS/ipmctl.spec --define "_topdir $SOURCEDIR/output/rpmbuild" ipmctl-03.00.00.0485/src/000077500000000000000000000000001440615110200144025ustar00rootroot00000000000000ipmctl-03.00.00.0485/src/os/000077500000000000000000000000001440615110200150235ustar00rootroot00000000000000ipmctl-03.00.00.0485/src/os/cli_cmds/000077500000000000000000000000001440615110200166005ustar00rootroot00000000000000ipmctl-03.00.00.0485/src/os/cli_cmds/DumpSupportCommand.c000066400000000000000000000226461440615110200225570ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "DumpSupportCommand.h" #include "NvmDimmCli.h" #include "NvmInterface.h" #include "LoadCommand.h" #include "Debug.h" #include "Convert.h" #include extern EFI_SHELL_PARAMETERS_PROTOCOL gOsShellParametersProtocol; /** Get FW debug log syntax definition **/ struct Command DumpSupportCommandSyntax = { DUMP_VERB, //!< verb { //!< options { L"", DESTINATION_PREFIX_OPTION, L"", L"",L"Destination to Dump the Support", FALSE, ValueRequired }, { VERBOSE_OPTION_SHORT, VERBOSE_OPTION, L"", L"",HELP_VERBOSE_DETAILS_TEXT, FALSE, ValueEmpty }, { L"",PROTOCOL_OPTION_DDRT, L"", L"",HELP_DDRT_DETAILS_TEXT, FALSE, ValueEmpty}, { L"",PROTOCOL_OPTION_SMBUS, L"", L"",HELP_SMBUS_DETAILS_TEXT, FALSE, ValueEmpty}, #ifdef OS_BUILD { OUTPUT_OPTION_SHORT, OUTPUT_OPTION, L"", OUTPUT_OPTION_HELP, HELP_OPTIONS_DETAILS_TEXT, FALSE, ValueRequired }, #endif { L"", DICTIONARY_OPTION, L"", L"", L"Dictionary File", FALSE, ValueOptional } }, { //!< targets {SUPPORT_TARGET, L"", L"", TRUE, ValueEmpty} }, { //!< properties { L"", L"", L"", FALSE, ValueOptional } }, L"Capture a snapshot of the system state for support purposes", //!< help DumpSupportCommand, //!< run function TRUE }; typedef struct _DUMP_SUPPORT_CMD { CHAR16 cmd[100]; } DUMP_SUPPORT_CMD; #define MAX_PLAFORM_SUPPORT_CMDS 7 #define MAX_DIMM_SPECIFIC_CMDS 5 #define APPEND_TO_FILE_NAME L"platform_support_info" #define PLATFORM_INFO_STR L"Platform information" #define DIMM_SPECIFIC_INFO L"Dimm Specific information - UUID: " #define WITH_DIC_OPTION DICTIONARY_OPTION SPACE_FORMAT_STR_SPACE DEBUG_TARGET L" " DIMM_TARGET SPACE_FORMAT_HEX #define WITHOUT_DICT_OPTION DEBUG_TARGET L" " DIMM_TARGET SPACE_FORMAT_HEX #define STR_DUMP_DEST L"dump -destination %ls " DUMP_SUPPORT_CMD DumpPlatformLevelCmds[MAX_PLAFORM_SUPPORT_CMDS] = { {L"version" }, {L"show -memoryresources"}, {L"show -a -system -capabilities"}, {L"show -a -topology" }, {L"start -diagnostic"}, {L"show -system"}, }; DUMP_SUPPORT_CMD DumpCmdsPerDimm[MAX_DIMM_SPECIFIC_CMDS] = { {L"show -a -dimm 0x%04x"}, {L"show -a -sensor -dimm 0x%04x"}, {L"show -pcd -dimm 0x%04x"}, {L"show -error Media -dimm 0x%04x"}, {L"show -error Thermal -dimm 0x%04x"}, }; #define NEW_DUMP_ENTRY_HEADER L"/*\n* %ls\n*/\n" #define PER_DIMM_HEADER L"/*\n* %ls 0x%04x\n*/\n" /** Register syntax of create -support **/ EFI_STATUS RegisterDumpSupportCommand( ) { EFI_STATUS ReturnCode = EFI_SUCCESS; NVDIMM_ENTRY(); ReturnCode = RegisterCommand(&DumpSupportCommandSyntax); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } STATIC VOID PrintHeaderInfo(CHAR16 *printLine) { Print(L"\n/*************************************************************************************************/\n"); Print(L"/******************************** %ls ********************************/", printLine); Print(L"\n/*************************************************************************************************/\n"); } STATIC VOID PrintAndExecuteCommand(CHAR16 *pCmdInputWithDimmId) { EFI_STATUS ReturnCode = EFI_INVALID_PARAMETER; struct CommandInput Input; struct Command Command; if (NULL == pCmdInputWithDimmId) { NVDIMM_DBG("pCmdInputWithDimmId value is NULL"); return; } Print(NEW_DUMP_ENTRY_HEADER, pCmdInputWithDimmId); FillCommandInput(pCmdInputWithDimmId, &Input); ReturnCode = Parse(&Input, &Command); if (!EFI_ERROR(ReturnCode)) { /* parse success, now run the command */ ReturnCode = ExecuteCmd(&Command); } FreeCommandInput(&Input); } /** Dump support command @param[in] pCmd Command from CLI @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pCmd NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL Function failure **/ EFI_STATUS DumpSupportCommand( IN struct Command *pCmd ) { EFI_DCPMM_CONFIG2_PROTOCOL *pNvmDimmConfigProtocol = NULL; EFI_STATUS ReturnCode = EFI_SUCCESS; COMMAND_STATUS *pCommandStatus = NULL; CHAR16 *pDumpUserPath = NULL; CHAR16 *pPlatformSupportFileName = NULL; CHAR16 *pPrintDIMMHeaderInfo = NULL; UINT32 Index = 0; UINT32 DimmIndex = 0; DIMM_INFO *pDimms = NULL; UINT32 DimmCount = 0; CHAR8 *pPlatformSupportFilenameAscii = NULL; UINTN pPlatformSupportFilenameAsciiLength = 0; UINTN pPlatformSupportFilenameAsciiSize = 0; FILE *hFile = NULL; PRINT_CONTEXT *pPrinterCtx = NULL; CHAR16 *pDictUserPath = NULL; CHAR16 *pCmdInputWithDimmId = NULL; NVDIMM_ENTRY(); if (pCmd == NULL) { ReturnCode = EFI_INVALID_PARAMETER; NVDIMM_DBG("pCmd parameter is NULL.\n"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_NO_COMMAND); goto Finish; } pPrinterCtx = pCmd->pPrintCtx; /** Open Config protocol **/ ReturnCode = OpenNvmDimmProtocol(gNvmDimmConfigProtocolGuid, (VOID **)&pNvmDimmConfigProtocol, NULL); if (EFI_ERROR(ReturnCode)) { ReturnCode = EFI_NOT_FOUND; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_OPENING_CONFIG_PROTOCOL); goto Finish; } ReturnCode = GetDimmList(pNvmDimmConfigProtocol, pCmd, DIMM_INFO_CATEGORY_NONE, &pDimms, &DimmCount); if (EFI_ERROR(ReturnCode)) { if(ReturnCode == EFI_NOT_FOUND) { PRINTER_SET_MSG(pCmd->pPrintCtx, ReturnCode, CLI_INFO_NO_FUNCTIONAL_DIMMS); } goto Finish; } if (containsOption(pCmd, DICTIONARY_OPTION)) { pDictUserPath = getOptionValue(pCmd, DICTIONARY_OPTION); if (pDictUserPath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Could not get -dict value. Out of memory"); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, FORMAT_STR_NL, CLI_ERR_OUT_OF_MEMORY); goto Finish; } } /* Check -destination prefix option */ if (containsOption(pCmd, DESTINATION_PREFIX_OPTION)) { pDumpUserPath = getOptionValue(pCmd, DESTINATION_PREFIX_OPTION); if (pDumpUserPath == NULL) { ReturnCode = EFI_OUT_OF_RESOURCES; NVDIMM_ERR("Could not get -destination value. Out of memory."); PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } } else { ReturnCode = EFI_INVALID_PARAMETER; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_PARSER_ERR_INVALID_OPTION_VALUES); goto Finish; } pPlatformSupportFileName = CatSPrint(pDumpUserPath, L"_" FORMAT_STR L".txt", APPEND_TO_FILE_NAME); if (NULL == pPlatformSupportFileName) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } pPlatformSupportFilenameAsciiLength = StrLen(pPlatformSupportFileName) + 1; pPlatformSupportFilenameAsciiSize = pPlatformSupportFilenameAsciiLength * sizeof(CHAR8); if(NULL == pPlatformSupportFileName || NULL == (pPlatformSupportFilenameAscii = AllocatePool(pPlatformSupportFilenameAsciiSize))) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } ReturnCode = UnicodeStrToAsciiStrS(pPlatformSupportFileName, pPlatformSupportFilenameAscii, pPlatformSupportFilenameAsciiLength); if (EFI_ERROR(ReturnCode)) { goto Finish; } if(NULL == (hFile = fopen(pPlatformSupportFilenameAscii, "w+"))) { ReturnCode = EFI_OUT_OF_RESOURCES; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_ERR_OUT_OF_MEMORY); goto Finish; } gOsShellParametersProtocol.StdOut = (SHELL_FILE_HANDLE) hFile; PrintHeaderInfo(PLATFORM_INFO_STR); for(Index = 0; Index < MAX_PLAFORM_SUPPORT_CMDS; ++Index) { PrintAndExecuteCommand(DumpPlatformLevelCmds[Index].cmd); } for (DimmIndex = 0; DimmIndex < DimmCount; ++DimmIndex) { pPrintDIMMHeaderInfo = CatSPrint(NULL, DIMM_SPECIFIC_INFO FORMAT_STR, pDimms[DimmIndex].DimmUid); PrintHeaderInfo(pPrintDIMMHeaderInfo); FREE_POOL_SAFE(pPrintDIMMHeaderInfo); for (Index = 0; Index < MAX_DIMM_SPECIFIC_CMDS; ++Index) { pCmdInputWithDimmId = CatSPrint(NULL, DumpCmdsPerDimm[Index].cmd, pDimms[DimmIndex].DimmHandle); PrintAndExecuteCommand(pCmdInputWithDimmId); } if (pDictUserPath != NULL) { pCmdInputWithDimmId = CatSPrintClean(NULL, STR_DUMP_DEST, pDumpUserPath); pCmdInputWithDimmId = CatSPrintClean(pCmdInputWithDimmId, WITH_DIC_OPTION, pDictUserPath, pDimms[DimmIndex].DimmHandle); } else { pCmdInputWithDimmId = CatSPrintClean(NULL, STR_DUMP_DEST, pDumpUserPath); pCmdInputWithDimmId = CatSPrintClean(pCmdInputWithDimmId, WITHOUT_DICT_OPTION, pDimms[DimmIndex].DimmHandle); } PrintAndExecuteCommand(pCmdInputWithDimmId); FREE_POOL_SAFE(pCmdInputWithDimmId); } //end for dimmIndex fclose(gOsShellParametersProtocol.StdOut); gOsShellParametersProtocol.StdOut = stdout; PRINTER_SET_MSG(pPrinterCtx, ReturnCode, CLI_INFO_DUMP_SUPPORT_SUCCESS L"\n", pPlatformSupportFileName); Finish: PRINTER_PROCESS_SET_BUFFER(pPrinterCtx); FreeCommandStatus(&pCommandStatus); FREE_POOL_SAFE(pPlatformSupportFileName); FREE_POOL_SAFE(pPlatformSupportFilenameAscii); FREE_POOL_SAFE(pDumpUserPath); FREE_POOL_SAFE(pDictUserPath); NVDIMM_EXIT_I64(ReturnCode); return ReturnCode; } ipmctl-03.00.00.0485/src/os/cli_cmds/DumpSupportCommand.h000066400000000000000000000015101440615110200225470ustar00rootroot00000000000000/* * Copyright (c) 2018, Intel Corporation. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _DUMP_SUPPORT_COMMAND_H_ #define _DUMP_SUPPORT_COMMAND_H_ #include #include "NvmInterface.h" #include "Common.h" /** Register dump -support command @retval EFI_SUCCESS success @retval EFI_ABORTED registering failure @retval EFI_OUT_OF_RESOURCES memory allocation failure **/ EFI_STATUS RegisterDumpSupportCommand( ); /** Dump support data command @param[in] pCmd Command from CLI @retval EFI_SUCCESS Success @retval EFI_INVALID_PARAMETER pCmd NULL or invalid command line parameters @retval EFI_OUT_OF_RESOURCES Memory allocation failure @retval EFI_ABORTED invoking CONFIG_PROTOCOL Function failure **/ EFI_STATUS DumpSupportCommand( IN struct Command *pCmd ); #endif //_DUMP_SUPPORT_COMMAND_H_ ipmctl-03.00.00.0485/src/os/docs/000077500000000000000000000000001440615110200157535ustar00rootroot00000000000000ipmctl-03.00.00.0485/src/os/docs/Doxyfile_api.in000066400000000000000000003310501440615110200207210ustar00rootroot00000000000000# Doxyfile 1.8.17 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "Intel® Optane™ Persistent Memory (Intel® Optane™ PMem) Software Management API" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @IPMCTL_OS_API_DOC_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "Intel Optane Persistent Memory Software Management API" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all generated output in the proper direction. # Possible values are: None, LTR, RTL and Context. # The default value is: None. OUTPUT_TEXT_DIRECTION = None # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines (in the resulting output). You can put ^^ in the value part of an # alias to insert a newline as if a physical newline was in the original file. # When you need a literal { or } or , in the value part of an alias you have to # escape them by means of a backslash (\), this can lead to conflicts with the # commands \{ and \} for these it is advised to use the version @{ and @} or use # a double escape (\\{ and \\}) ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files), VHDL, tcl. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is # Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 0 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # (including Cygwin) ands Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = NO # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = YES # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = YES # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = YES # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. If # EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @CMAKE_CURRENT_SOURCE_DIR@/src/os/nvm_api/nvm_management.h \ @CMAKE_CURRENT_SOURCE_DIR@/src/os/nvm_api/nvm_types.h \ @CMAKE_CURRENT_SOURCE_DIR@/DcpmPkg/common/NvmSharedDefs.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: https://www.gnu.org/software/libiconv/) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), # *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen # C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd, # *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = @CMAKE_CURRENT_SOURCE_DIR@/Documentation/ipmctl/ # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the # clang parser (see: http://clang.llvm.org/) for more accurate parsing at the # cost of reduced performance. This can be particularly helpful with template # rich C++ code for which doxygen's built-in parser lacks the necessary type # information. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_OPTIONS = # If clang assisted parsing is enabled you can provide the clang parser with the # path to the compilation database (see: # http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files # were built. This is equivalent to specifying the "-p" option to a clang tool, # such as clang-check. These options will then be passed to the parser. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. CLANG_DATABASE_PATH = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = NO # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: https://developer.apple.com/xcode/), introduced with OSX # 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. # The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /