1 /*
2  * Copyright (c) 2024 Nuvoton Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nuvoton_npcx_drbg
8 
9 #include <errno.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/drivers/entropy.h>
12 #include <zephyr/pm/device.h>
13 
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(entropy_npcx_drbg, CONFIG_ENTROPY_LOG_LEVEL);
16 
17 #include "soc_ncl.h"
18 
19 /* Reseed after 100 number generations */
20 #define NPCX_DRBG_SECURITY_STRENGTH                                                                \
21 	((enum ncl_drbg_security_strength)CONFIG_ENTROPY_NPCX_DRBG_SECURITY_STRENGTH)
22 #define NPCX_DRBG_RESEED_INTERVAL CONFIG_ENTROPY_NPCX_DRBG_RESEED_INTERVAL
23 
24 #define NPCX_DRBG_HANDLE_SIZE DT_INST_PROP(0, context_buffer_size)
25 struct entropy_npcx_drbg_dev_data {
26 	struct k_sem sem_lock;
27 	uint8_t handle[NPCX_DRBG_HANDLE_SIZE] __aligned(4);
28 };
29 
30 /*
31  * The base address of the table that holds the function pointer for each
32  * DRBG API in ROM.
33  */
34 #define NPCX_NCL_DRBG_BASE_ADDR ((const struct npcx_ncl_drbg *)DT_INST_REG_ADDR_BY_IDX(0, 0))
35 /* The following table holds the function pointer for each DRBG API in NPCX ROM. */
36 struct npcx_ncl_drbg {
37 	/* Get the DRBG context size required by DRBG APIs. */
38 	uint32_t (*get_context_size)(void);
39 	/* Initialize DRBG context. */
40 	enum ncl_status (*init_context)(void *ctx);
41 	/* Power on/off DRBG module. */
42 	enum ncl_status (*power)(void *ctx, uint8_t enable);
43 	/* Finalize DRBG context. */
44 	enum ncl_status (*finalize_context)(void *ctx);
45 	/* Initialize the DRBG hardware module and enable interrupts. */
46 	enum ncl_status (*init)(void *ctx, bool int_enable);
47 	/*
48 	 * Configure DRBG, pres_resistance enables/disables (1/0) prediction
49 	 * resistance
50 	 */
51 	enum ncl_status (*config)(void *ctx, uint32_t reseed_interval, uint8_t pred_resistance);
52 	/*
53 	 * This routine creates a first instantiation of the DRBG mechanism
54 	 * parameters. The routine pulls an initial seed from the HW RNG module
55 	 * and resets the reseed counter. DRBG and SHA modules should be
56 	 * activated prior to the this operation.
57 	 */
58 	enum ncl_status (*instantiate)(void *ctx, enum ncl_drbg_security_strength sec_strength,
59 				       const uint8_t *pers_string, uint32_t pers_string_len);
60 	/* Uninstantiate DRBG module */
61 	enum ncl_status (*uninstantiate)(void *ctx);
62 	/* Reseeds the internal state of the given instantce */
63 	enum ncl_status (*reseed)(void *ctc, uint8_t *add_data, uint32_t add_data_len);
64 	/* Generates a random number from the current internal state. */
65 	enum ncl_status (*generate)(void *ctx, const uint8_t *add_data, uint32_t add_data_len,
66 				    uint8_t *out_buff, uint32_t out_buff_len);
67 	/* Clear all DRBG SSPs (Sensitive Security Parameters) in HW & driver */
68 	enum ncl_status (*clear)(void *ctx);
69 };
70 #define NPCX_NCL_DRBG ((const struct npcx_ncl_drbg *)NPCX_NCL_DRBG_BASE_ADDR)
71 
72 /* The 2nd index of the reg property holds the address of NCL_SHA_Power ROM API */
73 #define NPCX_NCL_SHA_POWER_ADDR ((const struct npcx_ncl_drbg *)DT_INST_REG_ADDR_BY_IDX(0, 1))
74 struct npcx_ncl_sha {
75 	/* Power on/off SHA module. */
76 	enum ncl_status (*power)(void *ctx, uint8_t on);
77 };
78 #define NPCX_NCL_SHA_POWER ((const struct npcx_ncl_sha *)NPCX_NCL_SHA_POWER_ADDR)
79 
entropy_npcx_drbg_enable_sha_power(void * ctx,bool enable)80 static int entropy_npcx_drbg_enable_sha_power(void *ctx, bool enable)
81 {
82 	enum ncl_status ncl_ret;
83 
84 	ncl_ret = NPCX_NCL_SHA_POWER->power(ctx, enable);
85 	if (ncl_ret != NCL_STATUS_OK) {
86 		LOG_ERR("Fail to %s SHA power: err 0x%02x", enable ? "enable" : "disable", ncl_ret);
87 		return -EIO;
88 	}
89 
90 	return 0;
91 }
92 
entropy_npcx_drbg_enable_drbg_power(void * ctx,bool enable)93 static int entropy_npcx_drbg_enable_drbg_power(void *ctx, bool enable)
94 {
95 	enum ncl_status ncl_ret;
96 
97 	ncl_ret = NPCX_NCL_DRBG->power(ctx, enable);
98 	if (ncl_ret != NCL_STATUS_OK) {
99 		LOG_ERR("Fail to %s DRBG power: err 0x%02x", enable ? "enable" : "disable",
100 			ncl_ret);
101 		return -EIO;
102 	}
103 
104 	return 0;
105 }
106 
entropy_npcx_drbg_get_entropy(const struct device * dev,uint8_t * buf,uint16_t len)107 static int entropy_npcx_drbg_get_entropy(const struct device *dev, uint8_t *buf, uint16_t len)
108 {
109 	struct entropy_npcx_drbg_dev_data *const data = dev->data;
110 	enum ncl_status ncl_ret;
111 	void *ctx = data->handle;
112 	int ret = 0;
113 
114 	k_sem_take(&data->sem_lock, K_FOREVER);
115 
116 	ret = entropy_npcx_drbg_enable_sha_power(ctx, true);
117 	if (ret != 0) {
118 		goto err_exit;
119 	}
120 
121 	ncl_ret = NPCX_NCL_DRBG->generate(ctx, NULL, 0, buf, len);
122 	if (ncl_ret != NCL_STATUS_OK) {
123 		LOG_ERR("Fail to generate: err 0x%02x", ncl_ret);
124 		ret = -EIO;
125 		goto err_exit;
126 	}
127 
128 	ret = entropy_npcx_drbg_enable_sha_power(ctx, false);
129 
130 err_exit:
131 	k_sem_give(&data->sem_lock);
132 
133 	return ret;
134 }
135 
entropy_npcx_drbg_init(const struct device * dev)136 static int entropy_npcx_drbg_init(const struct device *dev)
137 {
138 	struct entropy_npcx_drbg_dev_data *const data = dev->data;
139 	uint32_t handle_size_required;
140 	enum ncl_status ncl_ret;
141 	void *ctx = data->handle;
142 	int ret;
143 
144 	handle_size_required = NPCX_NCL_DRBG->get_context_size();
145 	if (handle_size_required != NPCX_DRBG_HANDLE_SIZE) {
146 		LOG_ERR("Unexpected NCL DRBG context_size = %d", handle_size_required);
147 		return -ENOSR;
148 	}
149 
150 	ret = entropy_npcx_drbg_enable_sha_power(ctx, true);
151 	if (ret != 0) {
152 		return ret;
153 	}
154 
155 	ret = entropy_npcx_drbg_enable_drbg_power(ctx, true);
156 	if (ret != 0) {
157 		return ret;
158 	}
159 
160 	ncl_ret = NPCX_NCL_DRBG->init_context(ctx);
161 	if (ncl_ret != NCL_STATUS_OK) {
162 		LOG_ERR("Fail to init ctx: err 0x%02x", ncl_ret);
163 		return -EIO;
164 	}
165 
166 	ncl_ret = NPCX_NCL_DRBG->init(ctx, false);
167 	if (ncl_ret != NCL_STATUS_OK) {
168 		LOG_ERR("Fail to init: err 0x%02x", ncl_ret);
169 		return -EIO;
170 	}
171 
172 	ncl_ret = NPCX_NCL_DRBG->config(ctx, NPCX_DRBG_RESEED_INTERVAL, false);
173 	if (ncl_ret != NCL_STATUS_OK) {
174 		LOG_ERR("Fail to config: err 0x%02x", ncl_ret);
175 		return -EIO;
176 	}
177 
178 	ncl_ret = NPCX_NCL_DRBG->instantiate(ctx, NPCX_DRBG_SECURITY_STRENGTH, NULL, 0);
179 	if (ncl_ret != NCL_STATUS_OK) {
180 		LOG_ERR("Fail to config: err 0x%02x", ncl_ret);
181 		return -EIO;
182 	}
183 
184 	ret = entropy_npcx_drbg_enable_sha_power(ctx, false);
185 	if (ret != 0) {
186 		return ret;
187 	}
188 
189 	/* Locking semaphore initialized to 1 (unlocked) */
190 	k_sem_init(&data->sem_lock, 1, 1);
191 
192 	return 0;
193 }
194 
195 #ifdef CONFIG_PM_DEVICE
entropy_npcx_drbg_suspend(const struct device * dev)196 static int entropy_npcx_drbg_suspend(const struct device *dev)
197 {
198 	struct entropy_npcx_drbg_dev_data *const data = dev->data;
199 	void *ctx = data->handle;
200 
201 	return entropy_npcx_drbg_enable_drbg_power(ctx, false);
202 }
203 
entropy_npcx_drbg_resume(const struct device * dev)204 static int entropy_npcx_drbg_resume(const struct device *dev)
205 {
206 	struct entropy_npcx_drbg_dev_data *const data = dev->data;
207 	void *ctx = data->handle;
208 
209 	return entropy_npcx_drbg_enable_drbg_power(ctx, true);
210 }
211 
entropy_npcx_drbg_pm_action(const struct device * dev,enum pm_device_action action)212 static int entropy_npcx_drbg_pm_action(const struct device *dev, enum pm_device_action action)
213 {
214 	switch (action) {
215 	case PM_DEVICE_ACTION_SUSPEND:
216 		return entropy_npcx_drbg_suspend(dev);
217 	case PM_DEVICE_ACTION_RESUME:
218 		return entropy_npcx_drbg_resume(dev);
219 	default:
220 		return -ENOTSUP;
221 	}
222 }
223 #endif /* CONFIG_PM_DEVICE */
224 
225 static DEVICE_API(entropy, entropy_npcx_drbg_api) = {
226 	.get_entropy = entropy_npcx_drbg_get_entropy,
227 };
228 
229 static struct entropy_npcx_drbg_dev_data entropy_npcx_drbg_data;
230 
231 PM_DEVICE_DT_INST_DEFINE(0, entropy_npcx_drbg_pm_action);
232 
233 DEVICE_DT_INST_DEFINE(0, entropy_npcx_drbg_init, PM_DEVICE_DT_INST_GET(0), &entropy_npcx_drbg_data,
234 		      NULL, PRE_KERNEL_1, CONFIG_ENTROPY_INIT_PRIORITY, &entropy_npcx_drbg_api);
235