dfu_ble_svc.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
  2. *
  3. * The information contained herein is property of Nordic Semiconductor ASA.
  4. * Terms and conditions of usage are described in detail in NORDIC
  5. * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
  6. *
  7. * Licensees are granted free, non-transferable use of the information. NO
  8. * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
  9. * the file.
  10. *
  11. */
  12. #include "dfu_ble_svc.h"
  13. #include <string.h>
  14. #include "nrf_error.h"
  15. #include "crc16.h"
  16. #if defined ( __CC_ARM )
  17. static dfu_ble_peer_data_t m_peer_data __attribute__((section("NoInit"), zero_init)); /**< This variable should be placed in a non initialized RAM section in order to be valid upon soft reset from application into bootloader. */
  18. static uint16_t m_peer_data_crc __attribute__((section("NoInit"), zero_init)); /**< CRC variable to ensure the integrity of the peer data provided. */
  19. #elif defined ( __GNUC__ )
  20. __attribute__((section(".noinit"))) static dfu_ble_peer_data_t m_peer_data; /**< This variable should be placed in a non initialized RAM section in order to be valid upon soft reset from application into bootloader. */
  21. __attribute__((section(".noinit"))) static uint16_t m_peer_data_crc; /**< CRC variable to ensure the integrity of the peer data provided. */
  22. #elif defined ( __ICCARM__ )
  23. __no_init static dfu_ble_peer_data_t m_peer_data @ 0x20003F80; /**< This variable should be placed in a non initialized RAM section in order to be valid upon soft reset from application into bootloader. */
  24. __no_init static uint16_t m_peer_data_crc @ 0x20003F80 + sizeof(dfu_ble_peer_data_t); /**< CRC variable to ensure the integrity of the peer data provided. */
  25. #endif
  26. /**@brief Function for setting the peer data from application in bootloader before reset.
  27. *
  28. * @param[in] p_peer_data Pointer to the peer data containing keys for the connection.
  29. *
  30. * @retval NRF_SUCCES The data was set succesfully.
  31. * @retval NRF_ERROR_NULL If a null pointer was passed as argument.
  32. */
  33. static uint32_t dfu_ble_peer_data_set(dfu_ble_peer_data_t * p_peer_data)
  34. {
  35. if (p_peer_data == NULL)
  36. {
  37. return NRF_ERROR_NULL;
  38. }
  39. uint32_t src = (uint32_t)p_peer_data;
  40. uint32_t dst = (uint32_t)&m_peer_data;
  41. // Calculating length in order to check if destination is residing inside source.
  42. // Source inside the the destination (calculation underflow) is safe a source is read before
  43. // written to destination so that when destination grows into source, the source data is no
  44. // longer needed.
  45. uint32_t len = dst - src;
  46. if (src == dst)
  47. {
  48. // Do nothing as source and destination are identical, just calculate crc below.
  49. }
  50. else if (len < sizeof(dfu_ble_peer_data_t))
  51. {
  52. uint32_t i = 0;
  53. dst += sizeof(dfu_ble_peer_data_t);
  54. src += sizeof(dfu_ble_peer_data_t);
  55. // Copy byte wise backwards when facing overlapping structures.
  56. while (i++ <= sizeof(dfu_ble_peer_data_t))
  57. {
  58. *((uint8_t *)dst--) = *((uint8_t *)src--);
  59. }
  60. }
  61. else
  62. {
  63. memcpy((void *)dst, (void *)src, sizeof(dfu_ble_peer_data_t));
  64. }
  65. m_peer_data_crc = crc16_compute((uint8_t *)&m_peer_data, sizeof(m_peer_data), NULL);
  66. return NRF_SUCCESS;
  67. }
  68. /**@brief Function for handling second stage of SuperVisor Calls (SVC).
  69. *
  70. * @details The function will use svc_num to call the corresponding SVC function.
  71. *
  72. * @param[in] svc_num SVC number for function to be executed
  73. * @param[in] p_svc_args Argument list for the SVC.
  74. *
  75. * @return This function returns the error value of the SVC return. For further details, please
  76. * refer to the details of the SVC implementation itself.
  77. * @ref NRF_ERROR_SVC_HANDLER_MISSING is returned if no SVC handler is implemented for the
  78. * provided svc_num.
  79. */
  80. void C_SVC_Handler(uint8_t svc_num, uint32_t * p_svc_args)
  81. {
  82. switch (svc_num)
  83. {
  84. case DFU_BLE_SVC_PEER_DATA_SET:
  85. p_svc_args[0] = dfu_ble_peer_data_set((dfu_ble_peer_data_t *)p_svc_args[0]);
  86. break;
  87. default:
  88. p_svc_args[0] = NRF_ERROR_SVC_HANDLER_MISSING;
  89. break;
  90. }
  91. }
  92. /**@brief Function for handling the first stage of SuperVisor Calls (SVC) in assembly.
  93. *
  94. * @details The function will use the link register (LR) to determine the stack (PSP or MSP) to be
  95. * used and then decode the SVC number afterwards. After decoding the SVC number then
  96. * @ref C_SVC_Handler is called for further processing of the SVC.
  97. */
  98. #if defined ( __CC_ARM )
  99. __asm void SVC_Handler(void)
  100. {
  101. EXC_RETURN_CMD_PSP EQU 0xFFFFFFFD ; EXC_RETURN using PSP for ARM Cortex. If Link register contains this value it indicates the PSP was used before the SVC, otherwise the MSP was used.
  102. IMPORT C_SVC_Handler
  103. LDR R0, =EXC_RETURN_CMD_PSP ; Load the EXC_RETURN into R0 to be able to compare against LR to determine stack pointer used.
  104. CMP R0, LR ; Compare the link register with R0. If equal then PSP was used, otherwise MSP was used before SVC.
  105. BNE UseMSP ; Branch to code fetching SVC arguments using MSP.
  106. MRS R1, PSP ; Move PSP into R1.
  107. B Call_C_SVC_Handler ; Branch to Call_C_SVC_Handler below.
  108. UseMSP
  109. MRS R1, MSP ; MSP was used, therefore Move MSP into R1.
  110. Call_C_SVC_Handler
  111. LDR R0, [R1, #24] ; The arguments for the SVC was stacked. R1 contains Stack Pointer, the values stacked before SVC are R0, R1, R2, R3, R12, LR, PC (Return address), xPSR.
  112. ; R1 contains current SP so the PC of the stacked frame is at SP + 6 words (24 bytes). We load the PC into R0.
  113. SUBS R0, #2 ; The PC before the SVC is in R0. We subtract 2 to get the address prior to the instruction executed where the SVC number is located.
  114. LDRB R0, [R0] ; SVC instruction low octet: Load the byte at the address before the PC to fetch the SVC number.
  115. LDR R2, =C_SVC_Handler ; Load address of C implementation of SVC handler.
  116. BX R2 ; Branch to C implementation of SVC handler. R0 is now the SVC number, R1 is the StackPointer where the arguments (R0-R3) of the original SVC are located.
  117. ALIGN
  118. }
  119. #elif defined ( __GNUC__ )
  120. void __attribute__ (( naked )) SVC_Handler(void)
  121. {
  122. const uint32_t exc_return = 0xFFFFFFFD; // EXC_RETURN using PSP for ARM Cortex. If Link register contains this value it indicates the PSP was used before the SVC, otherwise the MSP was used.
  123. __asm volatile(
  124. "cmp lr, %0\t\n" // Compare the link register with argument 0 (%0), which is exc_return. If equal then PSP was used, otherwise MSP was used before SVC.
  125. "bne UseMSP\t\n" // Branch to code fetching SVC arguments using MSP.
  126. "mrs r1, psp\t\n" // Move PSP into R1.
  127. "b Call_C_SVC_Handler\t\n" // Branch to Call_C_SVC_Handler below.
  128. "UseMSP: \t\n" //
  129. "mrs r1, msp\t\n" // MSP was used, therefore Move MSP into R1.
  130. "Call_C_SVC_Handler: \t\n" //
  131. "ldr r0, [r1, #24]\t\n" // The arguments for the SVC was stacked. R1 contains Stack Pointer, the values stacked before SVC are R0, R1, R2, R3, R12, LR, PC (Return address), xPSR.
  132. // R1 contains current SP so the PC of the stacked frame is at SP + 6 words (24 bytes). We load the PC into R0.
  133. "sub r0, r0, #2\t\n" // The PC before the SVC is in R0. We subtract 2 to get the address prior to the instruction executed where the SVC number is located.
  134. "ldrb r0, [r0]\t\n" // SVC instruction low octet: Load the byte at the address before the PC to fetch the SVC number.
  135. "bx %1\t\n" // Branch to C implementation of SVC handler, argument 1 (%1). R0 is now the SVC number, R1 is the StackPointer where the arguments (R0-R3) of the original SVC are located.
  136. ".align\t\n"
  137. :: "r" (exc_return), "r" (C_SVC_Handler) // Argument list for the gcc assembly. exc_return is %0, C_SVC_Handler is %1.
  138. : "r0", "r1" // List of register maintained manually.
  139. );
  140. }
  141. #elif defined ( __ICCARM__ )
  142. void SVC_Handler(void)
  143. {
  144. asm("movs r0, #0x02\n" // Load 0x02 into R6 to prepare for exec return test.
  145. "mvns r0, r0\n" // Invert R0 to obtain exec return code using PSP for ARM Cortex.
  146. "cmp lr, r0\n" // Compare the link register with argument 0 (%0), which is exc_return. If equal then PSP was used, otherwise MSP was used before SVC.
  147. "bne.n UseMSP\n" // Branch to code fetching SVC arguments using MSP.
  148. "mrs r1, psp\n" // Move PSP into R1.
  149. "b.n Call_C_SVC_Handler\t\n" // Branch to Call_C_SVC_Handler below.
  150. "UseMSP: \n" //
  151. "mrs r1, msp\n" // MSP was used, therefore Move MSP into R1.
  152. "Call_C_SVC_Handler: \n" //
  153. "ldr r0, [r1, #24]\n" // The arguments for the SVC was stacked. R1 contains Stack Pointer, the values stacked before SVC are R0, R1, R2, R3, R12, LR, PC (Return address), xPSR.
  154. // R1 contains current SP so the PC of the stacked frame is at SP + 6 words (24 bytes). We load the PC into R0.
  155. "subs r0, #0x02\n" // The PC before the SVC is in R0. We subtract 2 to get the address prior to the instruction executed where the SVC number is located.
  156. "ldrb r0, [r0]\n" // SVC instruction low octet: Load the byte at the address before the PC to fetch the SVC number.
  157. "bx %0\n" // Branch to C implementation of SVC handler, argument 1 (%1). R0 is now the SVC number, R1 is the StackPointer where the arguments (R0-R3) of the original SVC are located.
  158. :: "r" (C_SVC_Handler) // Argument list for the gcc assembly. C_SVC_Handler is %0.
  159. : "r0", "r1" // List of register maintained manually.
  160. );
  161. }
  162. #else
  163. #error Compiler not supported.
  164. #endif
  165. uint32_t dfu_ble_peer_data_get(dfu_ble_peer_data_t * p_peer_data)
  166. {
  167. uint16_t crc;
  168. if (p_peer_data == NULL)
  169. {
  170. return NRF_ERROR_NULL;
  171. }
  172. crc = crc16_compute((uint8_t *)&m_peer_data, sizeof(m_peer_data), NULL);
  173. if (crc != m_peer_data_crc)
  174. {
  175. return NRF_ERROR_INVALID_DATA;
  176. }
  177. *p_peer_data = m_peer_data;
  178. // corrupt CRC to invalidate shared information.
  179. m_peer_data_crc++;
  180. return NRF_SUCCESS;
  181. }