Commit 3f3c341a authored by Paul Beesley's avatar Paul Beesley Committed by Manish Pandey
Browse files

Remove dependency between SPM_MM and ENABLE_SPM build flags



There are two different implementations of Secure Partition
management in TF-A. One is based on the "Management Mode" (MM)
design, the other is based on the Secure Partition Client Interface
(SPCI) specification. Currently there is a dependency between their
build flags that shouldn't exist, making further development
harder than it should be. This patch removes that
dependency, making the two flags function independently.

Before: ENABLE_SPM=1 is required for using either implementation.
        By default, the SPCI-based implementation is enabled and
        this is overridden if SPM_MM=1.

After: ENABLE_SPM=1 enables the SPCI-based implementation.
       SPM_MM=1 enables the MM-based implementation.
       The two build flags are mutually exclusive.

Note that the name of the ENABLE_SPM flag remains a bit
ambiguous - this will be improved in a subsequent patch. For this
patch the intention was to leave the name as-is so that it is
easier to track the changes that were made.

Change-Id: I8e64ee545d811c7000f27e8dc8ebb977d670608a
Signed-off-by: default avatarPaul Beesley <paul.beesley@arm.com>
parent b8e17967
...@@ -571,6 +571,14 @@ ifeq ($(CTX_INCLUDE_MTE_REGS),1) ...@@ -571,6 +571,14 @@ ifeq ($(CTX_INCLUDE_MTE_REGS),1)
endif endif
endif endif
# The SPCI-based SPM implementation and the MM-based SPM implementation cannot
# be enabled at the same time.
ifeq ($(ENABLE_SPM),1)
ifeq ($(SPM_MM),1)
$(error Use only one of the ENABLE_SPM and SPM_MM flags)
endif
endif
################################################################################ ################################################################################
# Process platform overrideable behaviour # Process platform overrideable behaviour
################################################################################ ################################################################################
......
...@@ -142,7 +142,7 @@ SECTIONS ...@@ -142,7 +142,7 @@ SECTIONS
ASSERT(__CPU_OPS_END__ > __CPU_OPS_START__, ASSERT(__CPU_OPS_END__ > __CPU_OPS_START__,
"cpu_ops not defined for this platform.") "cpu_ops not defined for this platform.")
#if ENABLE_SPM #if ENABLE_SPM || SPM_MM
#ifndef SPM_SHIM_EXCEPTIONS_VMA #ifndef SPM_SHIM_EXCEPTIONS_VMA
#define SPM_SHIM_EXCEPTIONS_VMA RAM #define SPM_SHIM_EXCEPTIONS_VMA RAM
#endif #endif
......
...@@ -5,22 +5,22 @@ ...@@ -5,22 +5,22 @@
# #
################################################################################ ################################################################################
# Include SPM Makefile # Include Makefile for either of the supported SPM implementations
################################################################################ ################################################################################
ifeq (${ENABLE_SPM},1) ifeq (${ENABLE_SPM},1)
ifeq (${SPM_MM},1) $(info Including SPM (SPCI) makefile)
ifeq (${EL3_EXCEPTION_HANDLING},0) include services/std_svc/spm/spm.mk
$(error EL3_EXCEPTION_HANDLING must be 1 for SPM support) endif
endif
$(info Including makefile of SPM based on MM) ifeq (${SPM_MM},1)
include services/std_svc/spm_mm/spm.mk ifeq (${EL3_EXCEPTION_HANDLING},0)
$(error EL3_EXCEPTION_HANDLING must be 1 for SPM support)
else else
$(info Including SPM makefile) $(info Including SPM Management Mode (MM) makefile)
include services/std_svc/spm/spm.mk include services/std_svc/spm_mm/spm.mk
endif endif
endif endif
include lib/psci/psci_lib.mk include lib/psci/psci_lib.mk
BL31_SOURCES += bl31/bl31_main.c \ BL31_SOURCES += bl31/bl31_main.c \
......
...@@ -235,8 +235,9 @@ Common build options ...@@ -235,8 +235,9 @@ Common build options
The default is 1 but is automatically disabled when the target architecture The default is 1 but is automatically disabled when the target architecture
is AArch32. is AArch32.
- ``ENABLE_SPM`` : Boolean option to enable the Secure Partition Manager (SPM). - ``ENABLE_SPM`` : Boolean option to enable the SPCI-based Secure Partition
Refer to :ref:`Secure Partition Manager` for more details about Manager (SPM) implementation.
Refer to the :ref:`Secure Partition Manager` guide for more details about
this feature. Default is 0. this feature. Default is 0.
- ``ENABLE_SVE_FOR_NS``: Boolean option to enable Scalable Vector Extension - ``ENABLE_SVE_FOR_NS``: Boolean option to enable Scalable Vector Extension
...@@ -507,6 +508,9 @@ Common build options ...@@ -507,6 +508,9 @@ Common build options
firmware images have been loaded in memory, and the MMU and caches are firmware images have been loaded in memory, and the MMU and caches are
turned off. Refer to the "Debugging options" section for more details. turned off. Refer to the "Debugging options" section for more details.
- ``SPM_MM`` : Boolean option to enable the Management Mode (MM)-based Secure
Partition Manager (SPM) implementation. The default value is ``0``.
- ``SP_MIN_WITH_SECURE_FIQ``: Boolean flag to indicate the SP_MIN handles - ``SP_MIN_WITH_SECURE_FIQ``: Boolean flag to indicate the SP_MIN handles
secure interrupts (caught through the FIQ line). Platforms can enable secure interrupts (caught through the FIQ line). Platforms can enable
this directive if they need to handle such interruption. When enabled, this directive if they need to handle such interruption. When enabled,
......
...@@ -500,9 +500,9 @@ ...@@ -500,9 +500,9 @@
* SPD and no SPM, as they are the only ones that can be used as BL32. * SPD and no SPM, as they are the only ones that can be used as BL32.
*/ */
#if defined(__aarch64__) && !JUNO_AARCH32_EL3_RUNTIME #if defined(__aarch64__) && !JUNO_AARCH32_EL3_RUNTIME
# if defined(SPD_none) && !ENABLE_SPM # if defined(SPD_none) && !ENABLE_SPM && !SPM_MM
# undef BL32_BASE # undef BL32_BASE
# endif /* defined(SPD_none) && !ENABLE_SPM */ # endif /* defined(SPD_none) && !ENABLE_SPM && !SPM_MM*/
#endif /* defined(__aarch64__) && !JUNO_AARCH32_EL3_RUNTIME */ #endif /* defined(__aarch64__) && !JUNO_AARCH32_EL3_RUNTIME */
/******************************************************************************* /*******************************************************************************
......
...@@ -38,7 +38,7 @@ typedef struct arm_tzc_regions_info { ...@@ -38,7 +38,7 @@ typedef struct arm_tzc_regions_info {
* - Region 1 with secure access only; * - Region 1 with secure access only;
* - the remaining DRAM regions access from the given Non-Secure masters. * - the remaining DRAM regions access from the given Non-Secure masters.
******************************************************************************/ ******************************************************************************/
#if ENABLE_SPM && SPM_MM #if SPM_MM
#define ARM_TZC_REGIONS_DEF \ #define ARM_TZC_REGIONS_DEF \
{ARM_AP_TZC_DRAM1_BASE, ARM_EL3_TZC_DRAM1_END, \ {ARM_AP_TZC_DRAM1_BASE, ARM_EL3_TZC_DRAM1_END, \
TZC_REGION_S_RDWR, 0}, \ TZC_REGION_S_RDWR, 0}, \
......
...@@ -178,11 +178,11 @@ RECLAIM_INIT_CODE := 0 ...@@ -178,11 +178,11 @@ RECLAIM_INIT_CODE := 0
# SPD choice # SPD choice
SPD := none SPD := none
# For including the Secure Partition Manager # Enable the SPCI-based Secure Partition Manager implementation
ENABLE_SPM := 0 ENABLE_SPM := 0
# Use the SPM based on MM # Enable the Management Mode (MM)-based Secure Partition Manager implementation
SPM_MM := 1 SPM_MM := 0
# Flag to introduce an infinite loop in BL1 just before it exits into the next # Flag to introduce an infinite loop in BL1 just before it exits into the next
# image. This is meant to help debugging the post-BL2 phase. # image. This is meant to help debugging the post-BL2 phase.
......
...@@ -96,10 +96,10 @@ const mmap_region_t plat_arm_mmap[] = { ...@@ -96,10 +96,10 @@ const mmap_region_t plat_arm_mmap[] = {
ARM_MAP_BL1_RW, ARM_MAP_BL1_RW,
#endif #endif
#endif /* TRUSTED_BOARD_BOOT */ #endif /* TRUSTED_BOARD_BOOT */
#if ENABLE_SPM && SPM_MM #if SPM_MM
ARM_SP_IMAGE_MMAP, ARM_SP_IMAGE_MMAP,
#endif #endif
#if ENABLE_SPM && !SPM_MM #if ENABLE_SPM
PLAT_MAP_SP_PACKAGE_MEM_RW, PLAT_MAP_SP_PACKAGE_MEM_RW,
#endif #endif
#if ARM_BL31_IN_DRAM #if ARM_BL31_IN_DRAM
...@@ -127,16 +127,16 @@ const mmap_region_t plat_arm_mmap[] = { ...@@ -127,16 +127,16 @@ const mmap_region_t plat_arm_mmap[] = {
MAP_DEVICE0, MAP_DEVICE0,
MAP_DEVICE1, MAP_DEVICE1,
ARM_V2M_MAP_MEM_PROTECT, ARM_V2M_MAP_MEM_PROTECT,
#if ENABLE_SPM && SPM_MM #if SPM_MM
ARM_SPM_BUF_EL3_MMAP, ARM_SPM_BUF_EL3_MMAP,
#endif #endif
#if ENABLE_SPM && !SPM_MM #if ENABLE_SPM
PLAT_MAP_SP_PACKAGE_MEM_RO, PLAT_MAP_SP_PACKAGE_MEM_RO,
#endif #endif
{0} {0}
}; };
#if ENABLE_SPM && defined(IMAGE_BL31) && SPM_MM #if defined(IMAGE_BL31) && SPM_MM
const mmap_region_t plat_arm_secure_partition_mmap[] = { const mmap_region_t plat_arm_secure_partition_mmap[] = {
V2M_MAP_IOFPGA_EL0, /* for the UART */ V2M_MAP_IOFPGA_EL0, /* for the UART */
MAP_REGION_FLAT(DEVICE0_BASE, \ MAP_REGION_FLAT(DEVICE0_BASE, \
...@@ -190,7 +190,7 @@ static unsigned int get_interconnect_master(void) ...@@ -190,7 +190,7 @@ static unsigned int get_interconnect_master(void)
} }
#endif #endif
#if ENABLE_SPM && defined(IMAGE_BL31) && SPM_MM #if defined(IMAGE_BL31) && SPM_MM
/* /*
* Boot information passed to a secure partition during initialisation. Linear * Boot information passed to a secure partition during initialisation. Linear
* indices in MP information will be filled at runtime. * indices in MP information will be filled at runtime.
......
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
* plat_arm_mmap array defined for each BL stage. * plat_arm_mmap array defined for each BL stage.
*/ */
#if defined(IMAGE_BL31) #if defined(IMAGE_BL31)
# if ENABLE_SPM # if ENABLE_SPM || SPM_MM
# define PLAT_ARM_MMAP_ENTRIES 9 # define PLAT_ARM_MMAP_ENTRIES 9
# define MAX_XLAT_TABLES 9 # define MAX_XLAT_TABLES 9
# define PLAT_SP_IMAGE_MMAP_REGIONS 30 # define PLAT_SP_IMAGE_MMAP_REGIONS 30
...@@ -116,7 +116,7 @@ ...@@ -116,7 +116,7 @@
* calculated using the current BL31 PROGBITS debug size plus the sizes of * calculated using the current BL31 PROGBITS debug size plus the sizes of
* BL2 and BL1-RW * BL2 and BL1-RW
*/ */
#if ENABLE_SPM && !SPM_MM #if ENABLE_SPM
#define PLAT_ARM_MAX_BL31_SIZE UL(0x60000) #define PLAT_ARM_MAX_BL31_SIZE UL(0x60000)
#else #else
#define PLAT_ARM_MAX_BL31_SIZE UL(0x3B000) #define PLAT_ARM_MAX_BL31_SIZE UL(0x3B000)
......
...@@ -282,9 +282,7 @@ else # if AArch64 ...@@ -282,9 +282,7 @@ else # if AArch64
BL31_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1 BL31_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1
endif endif
ifeq (${ENABLE_SPM},1) ifeq (${ENABLE_SPM},1)
ifeq (${SPM_MM},0)
BL31_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1 BL31_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1
endif
endif endif
ifeq (${SPD},trusty) ifeq (${SPD},trusty)
BL31_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1 BL31_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1
......
/* /*
* Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
* *
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
*/ */
...@@ -24,7 +24,7 @@ ehf_pri_desc_t arm_exceptions[] = { ...@@ -24,7 +24,7 @@ ehf_pri_desc_t arm_exceptions[] = {
/* Normal priority SDEI */ /* Normal priority SDEI */
EHF_PRI_DESC(ARM_PRI_BITS, PLAT_SDEI_NORMAL_PRI), EHF_PRI_DESC(ARM_PRI_BITS, PLAT_SDEI_NORMAL_PRI),
#endif #endif
#if ENABLE_SPM #if ENABLE_SPM || SPM_MM
EHF_PRI_DESC(ARM_PRI_BITS, PLAT_SP_PRI), EHF_PRI_DESC(ARM_PRI_BITS, PLAT_SP_PRI),
#endif #endif
}; };
......
...@@ -249,14 +249,12 @@ PLAT_BL_COMMON_SOURCES += plat/arm/common/aarch64/arm_pauth.c \ ...@@ -249,14 +249,12 @@ PLAT_BL_COMMON_SOURCES += plat/arm/common/aarch64/arm_pauth.c \
endif endif
# SPM uses libfdt in Arm platforms # SPM uses libfdt in Arm platforms
ifeq (${SPM_MM},0)
ifeq (${ENABLE_SPM},1) ifeq (${ENABLE_SPM},1)
BL31_SOURCES += common/fdt_wrappers.c \ BL31_SOURCES += common/fdt_wrappers.c \
plat/common/plat_spm_rd.c \ plat/common/plat_spm_rd.c \
plat/common/plat_spm_sp.c \ plat/common/plat_spm_sp.c \
${LIBFDT_SRCS} ${LIBFDT_SRCS}
endif endif
endif
ifneq (${TRUSTED_BOARD_BOOT},0) ifneq (${TRUSTED_BOARD_BOOT},0)
......
/* /*
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved.
* *
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
*/ */
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
* plat_arm_mmap array defined for each BL stage. * plat_arm_mmap array defined for each BL stage.
*/ */
#if defined(IMAGE_BL31) #if defined(IMAGE_BL31)
# if ENABLE_SPM # if ENABLE_SPM || SPM_MM
# define PLAT_ARM_MMAP_ENTRIES 9 # define PLAT_ARM_MMAP_ENTRIES 9
# define MAX_XLAT_TABLES 7 # define MAX_XLAT_TABLES 7
# define PLAT_SP_IMAGE_MMAP_REGIONS 7 # define PLAT_SP_IMAGE_MMAP_REGIONS 7
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
#elif defined(IMAGE_BL2U) #elif defined(IMAGE_BL2U)
# define PLATFORM_STACK_SIZE 0x400 # define PLATFORM_STACK_SIZE 0x400
#elif defined(IMAGE_BL31) #elif defined(IMAGE_BL31)
# if ENABLE_SPM # if ENABLE_SPM || SPM_MM
# define PLATFORM_STACK_SIZE 0x500 # define PLATFORM_STACK_SIZE 0x500
# else # else
# define PLATFORM_STACK_SIZE 0x400 # define PLATFORM_STACK_SIZE 0x400
......
/* /*
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved.
* *
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
*/ */
...@@ -46,7 +46,7 @@ const mmap_region_t plat_arm_mmap[] = { ...@@ -46,7 +46,7 @@ const mmap_region_t plat_arm_mmap[] = {
#if ARM_BL31_IN_DRAM #if ARM_BL31_IN_DRAM
ARM_MAP_BL31_SEC_DRAM, ARM_MAP_BL31_SEC_DRAM,
#endif #endif
#if ENABLE_SPM #if SPM_MM
ARM_SP_IMAGE_MMAP, ARM_SP_IMAGE_MMAP,
#endif #endif
#if TRUSTED_BOARD_BOOT && !BL2_AT_EL3 #if TRUSTED_BOARD_BOOT && !BL2_AT_EL3
...@@ -61,13 +61,13 @@ const mmap_region_t plat_arm_mmap[] = { ...@@ -61,13 +61,13 @@ const mmap_region_t plat_arm_mmap[] = {
V2M_MAP_IOFPGA, V2M_MAP_IOFPGA,
CSS_SGI_MAP_DEVICE, CSS_SGI_MAP_DEVICE,
SOC_CSS_MAP_DEVICE, SOC_CSS_MAP_DEVICE,
#if ENABLE_SPM #if SPM_MM
ARM_SPM_BUF_EL3_MMAP, ARM_SPM_BUF_EL3_MMAP,
#endif #endif
{0} {0}
}; };
#if ENABLE_SPM && defined(IMAGE_BL31) #if SPM_MM && defined(IMAGE_BL31)
const mmap_region_t plat_arm_secure_partition_mmap[] = { const mmap_region_t plat_arm_secure_partition_mmap[] = {
PLAT_ARM_SECURE_MAP_DEVICE, PLAT_ARM_SECURE_MAP_DEVICE,
ARM_SP_IMAGE_MMAP, ARM_SP_IMAGE_MMAP,
...@@ -77,12 +77,12 @@ const mmap_region_t plat_arm_secure_partition_mmap[] = { ...@@ -77,12 +77,12 @@ const mmap_region_t plat_arm_secure_partition_mmap[] = {
ARM_SPM_BUF_EL0_MMAP, ARM_SPM_BUF_EL0_MMAP,
{0} {0}
}; };
#endif /* ENABLE_SPM && defined(IMAGE_BL31) */ #endif /* SPM_MM && defined(IMAGE_BL31) */
#endif #endif
ARM_CASSERT_MMAP ARM_CASSERT_MMAP
#if ENABLE_SPM && defined(IMAGE_BL31) #if SPM_MM && defined(IMAGE_BL31)
/* /*
* Boot information passed to a secure partition during initialisation. Linear * Boot information passed to a secure partition during initialisation. Linear
* indices in MP information will be filled at runtime. * indices in MP information will be filled at runtime.
...@@ -130,7 +130,7 @@ const struct secure_partition_boot_info *plat_get_secure_partition_boot_info( ...@@ -130,7 +130,7 @@ const struct secure_partition_boot_info *plat_get_secure_partition_boot_info(
{ {
return &plat_arm_secure_partition_boot_info; return &plat_arm_secure_partition_boot_info;
} }
#endif /* ENABLE_SPM && defined(IMAGE_BL31) */ #endif /* SPM_MM && defined(IMAGE_BL31) */
#if TRUSTED_BOARD_BOOT #if TRUSTED_BOARD_BOOT
int plat_get_mbedtls_heap(void **heap_addr, size_t *heap_size) int plat_get_mbedtls_heap(void **heap_addr, size_t *heap_size)
......
...@@ -95,7 +95,7 @@ LR_RO_DATA +0 ...@@ -95,7 +95,7 @@ LR_RO_DATA +0
/* cpu_ops must always be defined */ /* cpu_ops must always be defined */
ScatterAssert(ImageLength(__CPU_OPS__) > 0) ScatterAssert(ImageLength(__CPU_OPS__) > 0)
#if ENABLE_SPM #if ENABLE_SPM || SPM_MM
LR_SPM +0 LR_SPM +0
{ {
/* /*
......
# #
# Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. # Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved.
# #
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
# #
...@@ -57,7 +57,7 @@ BL31_SOURCES += $(PLAT_PATH)/drivers/scp/sq_scmi.c \ ...@@ -57,7 +57,7 @@ BL31_SOURCES += $(PLAT_PATH)/drivers/scp/sq_scmi.c \
drivers/arm/css/mhu/css_mhu_doorbell.c drivers/arm/css/mhu/css_mhu_doorbell.c
endif endif
ifeq (${ENABLE_SPM},1) ifeq (${SPM_MM},1)
$(eval $(call add_define,PLAT_EXTRA_LD_SCRIPT)) $(eval $(call add_define,PLAT_EXTRA_LD_SCRIPT))
BL31_SOURCES += $(PLAT_PATH)/sq_spm.c BL31_SOURCES += $(PLAT_PATH)/sq_spm.c
......
...@@ -159,7 +159,7 @@ void bl31_plat_runtime_setup(void) ...@@ -159,7 +159,7 @@ void bl31_plat_runtime_setup(void)
void bl31_plat_arch_setup(void) void bl31_plat_arch_setup(void)
{ {
static const mmap_region_t secure_partition_mmap[] = { static const mmap_region_t secure_partition_mmap[] = {
#if ENABLE_SPM && SPM_MM #if SPM_MM
MAP_REGION_FLAT(PLAT_SPM_BUF_BASE, MAP_REGION_FLAT(PLAT_SPM_BUF_BASE,
PLAT_SPM_BUF_SIZE, PLAT_SPM_BUF_SIZE,
MT_RW_DATA | MT_SECURE), MT_RW_DATA | MT_SECURE),
...@@ -173,7 +173,7 @@ void bl31_plat_arch_setup(void) ...@@ -173,7 +173,7 @@ void bl31_plat_arch_setup(void)
sq_mmap_setup(BL31_BASE, BL31_SIZE, secure_partition_mmap); sq_mmap_setup(BL31_BASE, BL31_SIZE, secure_partition_mmap);
enable_mmu_el3(XLAT_TABLE_NC); enable_mmu_el3(XLAT_TABLE_NC);
#if ENABLE_SPM && SPM_MM #if SPM_MM
memcpy((void *)SPM_SHIM_EXCEPTIONS_START, memcpy((void *)SPM_SHIM_EXCEPTIONS_START,
(void *)SPM_SHIM_EXCEPTIONS_LMA, (void *)SPM_SHIM_EXCEPTIONS_LMA,
(uintptr_t)SPM_SHIM_EXCEPTIONS_END - (uintptr_t)SPM_SHIM_EXCEPTIONS_END -
......
...@@ -45,7 +45,7 @@ static int32_t std_svc_setup(void) ...@@ -45,7 +45,7 @@ static int32_t std_svc_setup(void)
ret = 1; ret = 1;
} }
#if ENABLE_SPM #if ENABLE_SPM || SPM_MM
if (spm_setup() != 0) { if (spm_setup() != 0) {
ret = 1; ret = 1;
} }
...@@ -103,7 +103,7 @@ static uintptr_t std_svc_smc_handler(uint32_t smc_fid, ...@@ -103,7 +103,7 @@ static uintptr_t std_svc_smc_handler(uint32_t smc_fid,
SMC_RET1(handle, ret); SMC_RET1(handle, ret);
} }
#if ENABLE_SPM && SPM_MM #if SPM_MM
/* /*
* Dispatch SPM calls to SPM SMC handler and return its return * Dispatch SPM calls to SPM SMC handler and return its return
* value * value
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment