From b6f821d72879d50f68a5d6eb4554f877dac919bd Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 13 Jun 2017 10:08:31 -0700 Subject: [PATCH] NcpBase: Implement support for PEEK and POKE spinel commands (#1899) The configuration option `OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE` enables/disables the peek/poke commands feature by NCP. By default it is disabled. If enabled, platform code can restrict access to certain memory regions using separate peek and poke delegate function pointers. --- include/openthread/ncp.h | 32 +++++ src/core/openthread-core-default-config.h | 14 +- src/ncp/ncp_base.cpp | 154 +++++++++++++++++++++- src/ncp/ncp_base.hpp | 20 ++- 4 files changed, 214 insertions(+), 6 deletions(-) diff --git a/include/openthread/ncp.h b/include/openthread/ncp.h index eb0667436..160737bf5 100644 --- a/include/openthread/ncp.h +++ b/include/openthread/ncp.h @@ -83,6 +83,38 @@ void otNcpInit(otInstance *aInstance); */ otError otNcpStreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen); +//----------------------------------------------------------------------------------------- +// Peek/Poke memory access control delegates + +/** + * Defines delegate (function pointer) type to control behavior of peek/poke operation. + * + * This delegate function is called to decide whether to allow peek or poke of a specific memory region. It is used + * if NCP support for peek/poke commands is enabled. + * + * @param[in] aAddress Start address of memory region. + * @param[in] aCount Number of bytes to peek or poke. + * + * @returns TRUE to allow peek/poke of the given memory region, FALSE otherwise. + * + */ +typedef bool (*otNcpDelegateAllowPeekPoke)(uint32_t aAddress, uint16_t aCount); + +/** + * This method registers peek/poke delegate functions with NCP module. + * + * The delegate functions are called by NCP module to decide whether to allow peek or poke of a specific memory region. + * If the delegate pointer is set to NULL, it allows peek/poke operation for any address. + * + * @param[in] aAllowPeekDelegate Delegate function pointer for peek operation. + * @param[in] aAllowPeekDelegate Delegate function pointer for poke operation. + * + * @retval OT_ERROR_NONE Successfully registered delegate functions. + * @retval OT_ERROR_DISABLED_FEATURE Peek/Poke feature is disabled (by a build-time configuration option). + * + */ +otError otNcpRegisterPeekPokeDelagates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, + otNcpDelegateAllowPeekPoke aAllowPokeDelegate); //----------------------------------------------------------------------------------------- // Legacy network APIs diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index 7c530f64e..b4e1eb7bd 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -810,7 +810,19 @@ * */ #ifndef OPENTHREAD_CONFIG_SUPERVISION_MSG_NO_ACK_REQUEST -#define OPENTHREAD_CONFIG_SUPERVISION_MSG_NO_ACK_REQUEST 0 +#define OPENTHREAD_CONFIG_SUPERVISION_MSG_NO_ACK_REQUEST 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE + * + * Define as 1 to enable peek/poke functionality on NCP. + * + * Peek/Poke allows the host to read/write to memory addresses on NCP. This is intended for debugging. + * + */ +#ifndef OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE +#define OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE 0 #endif #endif // OPENTHREAD_CORE_DEFAULT_CONFIG_H_ diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 872d34ee6..c063cc0ca 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -104,6 +104,10 @@ const NcpBase::CommandHandlerEntry NcpBase::mCommandHandlerTable[] = { SPINEL_CMD_NET_SAVE, &NcpBase::CommandHandler_NET_SAVE }, { SPINEL_CMD_NET_CLEAR, &NcpBase::CommandHandler_NET_CLEAR }, { SPINEL_CMD_NET_RECALL, &NcpBase::CommandHandler_NET_RECALL }, +#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE + { SPINEL_CMD_PEEK, &NcpBase::CommandHandler_PEEK }, + { SPINEL_CMD_POKE, &NcpBase::CommandHandler_POKE }, +#endif }; const NcpBase::GetPropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] = @@ -649,6 +653,10 @@ NcpBase::NcpBase(otInstance *aInstance): mHostPowerStateHeader(0), #if OPENTHREAD_ENABLE_JAM_DETECTION mShouldSignalJamStateChange(false), +#endif +#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE + mAllowPeekDelegate(NULL), + mAllowPokeDelegate(NULL), #endif mDroppedReplyTid(0), mDroppedReplyTidBitSet(0), @@ -2001,6 +2009,109 @@ otError NcpBase::CommandHandler_NET_RECALL(uint8_t header, unsigned int command, return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); } +#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE + +otError NcpBase::CommandHandler_PEEK(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len) +{ + spinel_ssize_t parsedLength; + uint32_t address; + uint16_t count; + spinel_status_t spinel_error = SPINEL_STATUS_OK; + otError errorCode = OT_ERROR_NONE; + + parsedLength = spinel_datatype_unpack( + arg_ptr, + arg_len, + ( + SPINEL_DATATYPE_UINT32_S // Address + SPINEL_DATATYPE_UINT16_S // Count + ), + &address, + &count + ); + + VerifyOrExit(parsedLength == arg_len, spinel_error = SPINEL_STATUS_PARSE_ERROR); + + VerifyOrExit(count != 0, spinel_error = SPINEL_STATUS_INVALID_ARGUMENT); + + if (mAllowPeekDelegate != NULL) + { + VerifyOrExit(mAllowPeekDelegate(address, count), spinel_error = SPINEL_STATUS_INVALID_ARGUMENT); + } + + SuccessOrExit(errorCode = OutboundFrameBegin()); + SuccessOrExit( + errorCode = OutboundFrameFeedPacked( + ( + SPINEL_DATATYPE_COMMAND_S // Header and Command + SPINEL_DATATYPE_UINT32_S // Address + SPINEL_DATATYPE_UINT16_S // Count + ), + header, + SPINEL_CMD_PEEK_RET, + address, + count + )); + SuccessOrExit(errorCode = OutboundFrameFeedData(reinterpret_cast(address), count)); + SuccessOrExit(errorCode = OutboundFrameSend()); + +exit: + if (spinel_error != SPINEL_STATUS_OK) + { + errorCode = SendLastStatus(header, spinel_error); + } + + (void)command; + + return errorCode; +} + +otError NcpBase::CommandHandler_POKE(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len) +{ + spinel_ssize_t parsedLength; + uint32_t address; + uint16_t count; + const uint8_t *data_ptr = NULL; + spinel_size_t data_len; + spinel_status_t spinel_error = SPINEL_STATUS_OK; + otError errorCode = OT_ERROR_NONE; + + parsedLength = spinel_datatype_unpack( + arg_ptr, + arg_len, + ( + SPINEL_DATATYPE_UINT32_S // Address + SPINEL_DATATYPE_UINT16_S // Count + SPINEL_DATATYPE_DATA_S // Bytes + ), + &address, + &count, + &data_ptr, + &data_len + ); + + VerifyOrExit(parsedLength == arg_len, spinel_error = SPINEL_STATUS_PARSE_ERROR); + + VerifyOrExit(count != 0, spinel_error = SPINEL_STATUS_INVALID_ARGUMENT); + VerifyOrExit(count <= data_len, spinel_error = SPINEL_STATUS_INVALID_ARGUMENT); + + if (mAllowPokeDelegate != NULL) + { + VerifyOrExit(mAllowPokeDelegate(address, count), spinel_error = SPINEL_STATUS_INVALID_ARGUMENT); + } + + memcpy(reinterpret_cast(address), data_ptr, count); + +exit: + errorCode = SendLastStatus(header, spinel_error); + + (void)command; + + return errorCode; +} + +#endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE + // ---------------------------------------------------------------------------- // MARK: Individual Property Getters // ---------------------------------------------------------------------------- @@ -2082,6 +2193,10 @@ otError NcpBase::GetPropertyHandler_CAPS(uint8_t header, spinel_prop_key_t key) SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_OOB_STEERING_DATA)); #endif +#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE + SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_PEEK_POKE)); +#endif + // TODO: Somehow get the following capability from the radio. SuccessOrExit( errorCode = OutboundFrameFeedPacked( @@ -7914,8 +8029,18 @@ otError NcpBase::StreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLe return errorCode; } -} // namespace ot +#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE +void NcpBase::RegisterPeekPokeDelagates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, + otNcpDelegateAllowPeekPoke aAllowPokeDelegate) +{ + mAllowPeekDelegate = aAllowPeekDelegate; + mAllowPokeDelegate = aAllowPokeDelegate; +} + +#endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE + +} // namespace ot // ---------------------------------------------------------------------------- // MARK: Virtual Datastream I/O (Public API) @@ -7934,6 +8059,33 @@ otError otNcpStreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen) return errorCode; } +// ---------------------------------------------------------------------------- +// MARK: Peek/Poke delegate API +// ---------------------------------------------------------------------------- + +otError otNcpRegisterPeekPokeDelagates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, + otNcpDelegateAllowPeekPoke aAllowPokeDelegate) +{ + otError errorCode = OT_ERROR_NONE; + +#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE + ot::NcpBase *ncp = ot::NcpBase::GetNcpInstance(); + + if (ncp != NULL) + { + ncp->RegisterPeekPokeDelagates(aAllowPeekDelegate, aAllowPokeDelegate); + } +#else + (void)aAllowPeekDelegate; + (void)aAllowPokeDelegate; + + errorCode = OT_ERROR_DISABLED_FEATURE; + +#endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE + + return errorCode; +} + // ---------------------------------------------------------------------------- // MARK: Legacy network APIs // ---------------------------------------------------------------------------- diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index 4d7d14c48..ccdd6ed9c 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -330,10 +330,12 @@ private: otError CommandHandler_PROP_VALUE_REMOVE(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len); otError CommandHandler_NET_SAVE(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len); - otError CommandHandler_NET_CLEAR(uint8_t header, unsigned int command, const uint8_t *arg_ptr, - uint16_t arg_len); - otError CommandHandler_NET_RECALL(uint8_t header, unsigned int command, const uint8_t *arg_ptr, - uint16_t arg_len); + otError CommandHandler_NET_CLEAR(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len); + otError CommandHandler_NET_RECALL(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len); +#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE + otError CommandHandler_PEEK(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len); + otError CommandHandler_POKE(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len); +#endif otError GetPropertyHandler_ChannelMaskHelper(uint8_t header, spinel_prop_key_t key, uint32_t channel_mask); @@ -673,6 +675,11 @@ private: public: otError StreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen); +#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE + void RegisterPeekPokeDelagates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, + otNcpDelegateAllowPeekPoke aAllowPokeDelegate); +#endif + #if OPENTHREAD_ENABLE_LEGACY public: void HandleLegacyNodeDidJoin(const otExtAddress *aExtAddr); @@ -710,6 +717,11 @@ private: bool mShouldSignalJamStateChange; #endif +#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE + otNcpDelegateAllowPeekPoke mAllowPeekDelegate; + otNcpDelegateAllowPeekPoke mAllowPokeDelegate; +#endif + spinel_tid_t mDroppedReplyTid; uint16_t mDroppedReplyTidBitSet; spinel_tid_t mNextExpectedTid;