1 /*
2  * Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _HARDWARE_SHA256_H
8 #define _HARDWARE_SHA256_H
9 
10 #include "pico.h"
11 #include "hardware/structs/sha256.h"
12 
13 /** \file hardware/sha256.h
14  *  \defgroup hardware_sha256 hardware_sha256
15  *
16  * \brief Hardware SHA-256 Accelerator API
17  *
18  * RP2350 is equipped with an implementation of the SHA-256 hash algorithm.
19  * The hardware should first be configured by calling the \ref sha256_set_dma_size and \ref sha256_set_bswap functions.
20  * To generate a new hash the hardware should first be initialised by calling \ref sha256_start.
21  * The hardware is ready to accept data when \ref sha256_is_ready returns true,
22  * at which point the data to be hashed can be written to the address returned by \ref sha256_get_write_addr.
23  * The hardware requires 64 bytes to be written in one go or else \ref sha256_err_not_ready will indicate an error and
24  * the hashing process must be restarted.
25  * \ref sha256_is_sum_valid will return true when there is a valid checksum result which can be retrieved by calling \ref sha256_get_result.
26  */
27 
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 
32 // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_HARDWARE_SHA256, Enable/disable hardware_sha256 assertions, type=bool, default=0, group=hardware_sha256
33 #ifndef PARAM_ASSERTIONS_ENABLED_HARDWARE_SHA256
34 #ifdef PARAM_ASSERTIONS_ENABLED_SHA256 // backwards compatibility with SDK < 2.0.0
35 #define PARAM_ASSERTIONS_ENABLED_HARDWARE_SHA256 PARAM_ASSERTIONS_ENABLED_SHA256
36 #else
37 #define PARAM_ASSERTIONS_ENABLED_HARDWARE_SHA256 0
38 #endif
39 #endif
40 
41 /*! \brief Size of a sha256 result in bytes.
42  *  \ingroup hardware_sha256
43  */
44 #define SHA256_RESULT_BYTES 32
45 
46 /*! \brief SHA-256 endianness definition used in the API
47  *  \ingroup hardware_sha256
48  */
49 enum sha256_endianness {
50     SHA256_LITTLE_ENDIAN, ///< Little Endian
51     SHA256_BIG_ENDIAN,    ///< Big Endian
52 };
53 
54 /*! \brief SHA-256 result generated by the API
55  *  \ingroup hardware_sha256
56  */
57 typedef union {
58     uint32_t words[SHA256_RESULT_BYTES/4];
59     uint8_t  bytes[SHA256_RESULT_BYTES];
60 } sha256_result_t;
61 
62 /*! \brief  Configure the correct DMA data size.
63  *  \ingroup hardware_sha256
64  *
65  * This must be configured before the DMA channel is triggered and ensures the correct number of transfers is requested per block.
66  *
67  * \param size_in_bytes Size of DMA transfers, either 1, 2 or 4 bytes only.
68  */
sha256_set_dma_size(uint size_in_bytes)69 static inline void sha256_set_dma_size(uint size_in_bytes) {
70     uint32_t val;
71     invalid_params_if(HARDWARE_SHA256, size_in_bytes != 1 && size_in_bytes != 2 && size_in_bytes != 4);
72     if (size_in_bytes == 1) {
73         val = SHA256_CSR_DMA_SIZE_VALUE_8BIT;
74     } else if (size_in_bytes == 2) {
75         val = SHA256_CSR_DMA_SIZE_VALUE_16BIT;
76     } else {
77         val = SHA256_CSR_DMA_SIZE_VALUE_32BIT;
78     }
79     hw_write_masked(&sha256_hw->csr, val << SHA256_CSR_DMA_SIZE_LSB, SHA256_CSR_DMA_SIZE_BITS);
80 }
81 
82 
83 /*! \brief  Enable or disable byte swapping of 32-bit values.
84  *  \ingroup hardware_sha256
85  *
86  * The SHA256 algorithm expects bytes in big endian order, but the system bus deals with little endian data,
87  * so control is provided to convert little endian bus data to big endian internal data. This defaults to true
88  *
89  * \param swap false to disable byte swapping
90  */
sha256_set_bswap(bool swap)91 static inline void sha256_set_bswap(bool swap) {
92     if (swap) {
93         hw_set_bits(&sha256_hw->csr, SHA256_CSR_BSWAP_BITS);
94     } else {
95         hw_clear_bits(&sha256_hw->csr, SHA256_CSR_BSWAP_BITS);
96     }
97 }
98 
99 /*! \brief  Prepare the hardware for a new checksum.
100  *  \ingroup hardware_sha256
101  *
102  * Called to initialise the hardware before starting the checksum calculation
103  */
sha256_start(void)104 static inline void sha256_start(void) {
105     hw_set_bits(&sha256_hw->csr, SHA256_CSR_START_BITS);
106 }
107 
108 /*! \brief  Check if a valid checksum has been calculated
109  *  \ingroup hardware_sha256
110  *
111  * The checksum result will be invalid when data is first written to the hardware,
112  * and then once 64 bytes of data has been written it may take some time to complete the digest of the current block.
113  * This function can be used to determine when the checksum is valid.
114  *
115  * \return True if \ref sha256_get_result would return a valid result
116  */
sha256_is_sum_valid(void)117 static inline bool sha256_is_sum_valid(void) {
118     return sha256_hw->csr & SHA256_CSR_SUM_VLD_BITS;
119 }
120 
121 /*! \brief  Check if a the hardware is ready to accept more data
122  *  \ingroup hardware_sha256
123  *
124  * After writing 64 bytes of data to the hardware, it will be unable to accept more data for a time.
125  * Call this to check if the hardware is ready for more data to be written. \see sha256_err_not_ready
126  *
127  * \return True if the hardware is ready to receive more data
128  */
sha256_is_ready(void)129 static inline bool sha256_is_ready(void) {
130     return sha256_hw->csr & SHA256_CSR_WDATA_RDY_BITS;
131 }
132 
133 /*! \brief  Wait until the checksum is valid
134  *  \ingroup hardware_sha256
135  *
136  * When a multiple of 64 bytes of data has been written to the hardware,
137  * the checksum will be valid once the digest of the current block is complete.
138  * This function waits until when the checksum result is valid.
139  */
sha256_wait_valid_blocking(void)140 static inline void sha256_wait_valid_blocking(void) {
141     while (!sha256_is_sum_valid()) {
142         tight_loop_contents();
143     }
144 }
145 
146 /*! \brief  Wait until the hardware is ready to accept more data
147  *  \ingroup hardware_sha256
148  *
149  * Before writing to the hardware, it's necessary to check it is ready to accept more data.
150  * This function waits until the hardware is ready to accept more data
151  */
sha256_wait_ready_blocking(void)152 static inline void sha256_wait_ready_blocking(void) {
153     while (!sha256_is_ready()) {
154         tight_loop_contents();
155     }
156 }
157 
158 /*! \brief  Get the checksum result
159  *  \ingroup hardware_sha256
160  *
161  * Read the 32 byte result calculated by the hardware. Only valid if \ref sha256_is_sum_valid is True
162  *
163  * \param out The checksum result
164  */
165 void sha256_get_result(sha256_result_t *out, enum sha256_endianness endianness);
166 
167 /*! \brief  Check if data was written before the hardware was ready
168  *  \ingroup hardware_sha256
169  *
170  * Indicates if an error has occurred due to data being written when the hardware is not ready.
171  *
172  * \return True if data was written before the hardware was ready
173  */
sha256_err_not_ready(void)174 static inline bool sha256_err_not_ready(void) {
175     return sha256_hw->csr & SHA256_CSR_ERR_WDATA_NOT_RDY_BITS;
176 }
177 
178 /*! \brief  Clear the "not ready" error condition
179  *  \ingroup hardware_sha256
180  *
181  * Resets the hardware if a "not ready" error condition is indicated.
182  */
sha256_err_not_ready_clear(void)183 static inline void sha256_err_not_ready_clear(void) {
184     hw_clear_bits(&sha256_hw->csr, SHA256_CSR_ERR_WDATA_NOT_RDY_BITS);
185 }
186 
187 /*! \brief  Address to write the data to be hashed
188  *  \ingroup hardware_sha256
189  *
190  * Returns the hardware address where data to be hashed should be written
191  *
192  * \return Address to write data to be hashed
193  */
sha256_get_write_addr(void)194 static inline volatile void *sha256_get_write_addr(void) {
195     return &sha256_hw->wdata;
196 }
197 
198 /*! \brief  Write one 32bit word of data to the SHA-256 hardware
199  *  \ingroup hardware_sha256
200  *
201  * \param word data to write
202  */
sha256_put_word(uint32_t word)203 static inline void sha256_put_word(uint32_t word) {
204     sha256_hw->wdata = word;
205 }
206 
207 /*! \brief  Write one byte of data to the SHA-256 hardware
208  *  \ingroup hardware_sha256
209  *
210  * \param b data to write
211  */
sha256_put_byte(uint8_t b)212 static inline void sha256_put_byte(uint8_t b) {
213     *((io_rw_8*)&sha256_hw->wdata) = b;
214 }
215 
216 #ifdef __cplusplus
217 }
218 #endif
219 
220 #endif
221