1389711b3SMarek Behún // SPDX-License-Identifier: GPL-2.0
2389711b3SMarek Behún /*
3389711b3SMarek Behún  * Turris Mox rWTM firmware driver
4389711b3SMarek Behún  *
52f4f8d17SMarek Behún  * Copyright (C) 2019, 2024 Marek Behún <kabel@kernel.org>
6389711b3SMarek Behún  */
7389711b3SMarek Behún 
8389711b3SMarek Behún #include <linux/armada-37xx-rwtm-mailbox.h>
9389711b3SMarek Behún #include <linux/completion.h>
1050524d78SMarek Behún #include <linux/debugfs.h>
11389711b3SMarek Behún #include <linux/dma-mapping.h>
12389711b3SMarek Behún #include <linux/hw_random.h>
13389711b3SMarek Behún #include <linux/mailbox_client.h>
14389711b3SMarek Behún #include <linux/module.h>
15389711b3SMarek Behún #include <linux/mutex.h>
16389711b3SMarek Behún #include <linux/of.h>
17389711b3SMarek Behún #include <linux/platform_device.h>
18389711b3SMarek Behún #include <linux/slab.h>
19389711b3SMarek Behún 
20389711b3SMarek Behún #define DRIVER_NAME		"turris-mox-rwtm"
21389711b3SMarek Behún 
22389711b3SMarek Behún /*
23389711b3SMarek Behún  * The macros and constants below come from Turris Mox's rWTM firmware code.
24389711b3SMarek Behún  * This firmware is open source and it's sources can be found at
25389711b3SMarek Behún  * https://gitlab.labs.nic.cz/turris/mox-boot-builder/tree/master/wtmi.
26389711b3SMarek Behún  */
27389711b3SMarek Behún 
28389711b3SMarek Behún #define MBOX_STS_SUCCESS	(0 << 30)
29389711b3SMarek Behún #define MBOX_STS_FAIL		(1 << 30)
30389711b3SMarek Behún #define MBOX_STS_BADCMD		(2 << 30)
31389711b3SMarek Behún #define MBOX_STS_ERROR(s)	((s) & (3 << 30))
32389711b3SMarek Behún #define MBOX_STS_VALUE(s)	(((s) >> 10) & 0xfffff)
33389711b3SMarek Behún #define MBOX_STS_CMD(s)		((s) & 0x3ff)
34389711b3SMarek Behún 
35389711b3SMarek Behún enum mbox_cmd {
36389711b3SMarek Behún 	MBOX_CMD_GET_RANDOM	= 1,
37389711b3SMarek Behún 	MBOX_CMD_BOARD_INFO	= 2,
38389711b3SMarek Behún 	MBOX_CMD_ECDSA_PUB_KEY	= 3,
39389711b3SMarek Behún 	MBOX_CMD_HASH		= 4,
40389711b3SMarek Behún 	MBOX_CMD_SIGN		= 5,
41389711b3SMarek Behún 	MBOX_CMD_VERIFY		= 6,
42389711b3SMarek Behún 
43389711b3SMarek Behún 	MBOX_CMD_OTP_READ	= 7,
44389711b3SMarek Behún 	MBOX_CMD_OTP_WRITE	= 8,
45389711b3SMarek Behún };
46389711b3SMarek Behún 
47389711b3SMarek Behún struct mox_kobject;
48389711b3SMarek Behún 
49389711b3SMarek Behún struct mox_rwtm {
50389711b3SMarek Behún 	struct device *dev;
51389711b3SMarek Behún 	struct mbox_client mbox_client;
52389711b3SMarek Behún 	struct mbox_chan *mbox;
53389711b3SMarek Behún 	struct mox_kobject *kobj;
54389711b3SMarek Behún 	struct hwrng hwrng;
55389711b3SMarek Behún 
56389711b3SMarek Behún 	struct armada_37xx_rwtm_rx_msg reply;
57389711b3SMarek Behún 
58389711b3SMarek Behún 	void *buf;
59389711b3SMarek Behún 	dma_addr_t buf_phys;
60389711b3SMarek Behún 
61389711b3SMarek Behún 	struct mutex busy;
62389711b3SMarek Behún 	struct completion cmd_done;
63389711b3SMarek Behún 
64389711b3SMarek Behún 	/* board information */
65389711b3SMarek Behún 	int has_board_info;
66389711b3SMarek Behún 	u64 serial_number;
67389711b3SMarek Behún 	int board_version, ram_size;
68389711b3SMarek Behún 	u8 mac_address1[6], mac_address2[6];
69389711b3SMarek Behún 
70389711b3SMarek Behún 	/* public key burned in eFuse */
71389711b3SMarek Behún 	int has_pubkey;
72389711b3SMarek Behún 	u8 pubkey[135];
7350524d78SMarek Behún 
7450524d78SMarek Behún #ifdef CONFIG_DEBUG_FS
7550524d78SMarek Behún 	/*
7650524d78SMarek Behún 	 * Signature process. This is currently done via debugfs, because it
7750524d78SMarek Behún 	 * does not conform to the sysfs standard "one file per attribute".
7850524d78SMarek Behún 	 * It should be rewritten via crypto API once akcipher API is available
7950524d78SMarek Behún 	 * from userspace.
8050524d78SMarek Behún 	 */
8150524d78SMarek Behún 	struct dentry *debugfs_root;
8250524d78SMarek Behún 	u32 last_sig[34];
8350524d78SMarek Behún 	int last_sig_done;
8450524d78SMarek Behún #endif
85389711b3SMarek Behún };
86389711b3SMarek Behún 
87389711b3SMarek Behún struct mox_kobject {
88389711b3SMarek Behún 	struct kobject kobj;
89389711b3SMarek Behún 	struct mox_rwtm *rwtm;
90389711b3SMarek Behún };
91389711b3SMarek Behún 
rwtm_to_kobj(struct mox_rwtm * rwtm)92389711b3SMarek Behún static inline struct kobject *rwtm_to_kobj(struct mox_rwtm *rwtm)
93389711b3SMarek Behún {
94389711b3SMarek Behún 	return &rwtm->kobj->kobj;
95389711b3SMarek Behún }
96389711b3SMarek Behún 
to_rwtm(struct kobject * kobj)97389711b3SMarek Behún static inline struct mox_rwtm *to_rwtm(struct kobject *kobj)
98389711b3SMarek Behún {
99389711b3SMarek Behún 	return container_of(kobj, struct mox_kobject, kobj)->rwtm;
100389711b3SMarek Behún }
101389711b3SMarek Behún 
mox_kobj_release(struct kobject * kobj)102389711b3SMarek Behún static void mox_kobj_release(struct kobject *kobj)
103389711b3SMarek Behún {
104389711b3SMarek Behún 	kfree(to_rwtm(kobj)->kobj);
105389711b3SMarek Behún }
106389711b3SMarek Behún 
107f326e72aSThomas Weißschuh static const struct kobj_type mox_kobj_ktype = {
108389711b3SMarek Behún 	.release	= mox_kobj_release,
109389711b3SMarek Behún 	.sysfs_ops	= &kobj_sysfs_ops,
110389711b3SMarek Behún };
111389711b3SMarek Behún 
mox_kobj_create(struct mox_rwtm * rwtm)112389711b3SMarek Behún static int mox_kobj_create(struct mox_rwtm *rwtm)
113389711b3SMarek Behún {
114389711b3SMarek Behún 	rwtm->kobj = kzalloc(sizeof(*rwtm->kobj), GFP_KERNEL);
115389711b3SMarek Behún 	if (!rwtm->kobj)
116389711b3SMarek Behún 		return -ENOMEM;
117389711b3SMarek Behún 
118389711b3SMarek Behún 	kobject_init(rwtm_to_kobj(rwtm), &mox_kobj_ktype);
119389711b3SMarek Behún 	if (kobject_add(rwtm_to_kobj(rwtm), firmware_kobj, "turris-mox-rwtm")) {
120389711b3SMarek Behún 		kobject_put(rwtm_to_kobj(rwtm));
121389711b3SMarek Behún 		return -ENXIO;
122389711b3SMarek Behún 	}
123389711b3SMarek Behún 
124389711b3SMarek Behún 	rwtm->kobj->rwtm = rwtm;
125389711b3SMarek Behún 
126389711b3SMarek Behún 	return 0;
127389711b3SMarek Behún }
128389711b3SMarek Behún 
129389711b3SMarek Behún #define MOX_ATTR_RO(name, format, cat)				\
130389711b3SMarek Behún static ssize_t							\
131389711b3SMarek Behún name##_show(struct kobject *kobj, struct kobj_attribute *a,	\
132389711b3SMarek Behún 	    char *buf)						\
133389711b3SMarek Behún {								\
134389711b3SMarek Behún 	struct mox_rwtm *rwtm = to_rwtm(kobj);	\
135389711b3SMarek Behún 	if (!rwtm->has_##cat)					\
136389711b3SMarek Behún 		return -ENODATA;				\
137389711b3SMarek Behún 	return sprintf(buf, format, rwtm->name);		\
138389711b3SMarek Behún }								\
139389711b3SMarek Behún static struct kobj_attribute mox_attr_##name = __ATTR_RO(name)
140389711b3SMarek Behún 
141389711b3SMarek Behún MOX_ATTR_RO(serial_number, "%016llX\n", board_info);
142389711b3SMarek Behún MOX_ATTR_RO(board_version, "%i\n", board_info);
143389711b3SMarek Behún MOX_ATTR_RO(ram_size, "%i\n", board_info);
144389711b3SMarek Behún MOX_ATTR_RO(mac_address1, "%pM\n", board_info);
145389711b3SMarek Behún MOX_ATTR_RO(mac_address2, "%pM\n", board_info);
146389711b3SMarek Behún MOX_ATTR_RO(pubkey, "%s\n", pubkey);
147389711b3SMarek Behún 
mox_get_status(enum mbox_cmd cmd,u32 retval)148389711b3SMarek Behún static int mox_get_status(enum mbox_cmd cmd, u32 retval)
149389711b3SMarek Behún {
150e34e6025SMarek Behún 	if (MBOX_STS_CMD(retval) != cmd)
151389711b3SMarek Behún 		return -EIO;
152389711b3SMarek Behún 	else if (MBOX_STS_ERROR(retval) == MBOX_STS_FAIL)
153389711b3SMarek Behún 		return -(int)MBOX_STS_VALUE(retval);
154e34e6025SMarek Behún 	else if (MBOX_STS_ERROR(retval) == MBOX_STS_BADCMD)
155e34e6025SMarek Behún 		return -ENOSYS;
156e34e6025SMarek Behún 	else if (MBOX_STS_ERROR(retval) != MBOX_STS_SUCCESS)
157e34e6025SMarek Behún 		return -EIO;
158389711b3SMarek Behún 	else
159389711b3SMarek Behún 		return MBOX_STS_VALUE(retval);
160389711b3SMarek Behún }
161389711b3SMarek Behún 
162389711b3SMarek Behún static const struct attribute *mox_rwtm_attrs[] = {
163389711b3SMarek Behún 	&mox_attr_serial_number.attr,
164389711b3SMarek Behún 	&mox_attr_board_version.attr,
165389711b3SMarek Behún 	&mox_attr_ram_size.attr,
166389711b3SMarek Behún 	&mox_attr_mac_address1.attr,
167389711b3SMarek Behún 	&mox_attr_mac_address2.attr,
168389711b3SMarek Behún 	&mox_attr_pubkey.attr,
169389711b3SMarek Behún 	NULL
170389711b3SMarek Behún };
171389711b3SMarek Behún 
mox_rwtm_rx_callback(struct mbox_client * cl,void * data)172389711b3SMarek Behún static void mox_rwtm_rx_callback(struct mbox_client *cl, void *data)
173389711b3SMarek Behún {
174389711b3SMarek Behún 	struct mox_rwtm *rwtm = dev_get_drvdata(cl->dev);
175389711b3SMarek Behún 	struct armada_37xx_rwtm_rx_msg *msg = data;
176389711b3SMarek Behún 
1772f4f8d17SMarek Behún 	if (completion_done(&rwtm->cmd_done))
1782f4f8d17SMarek Behún 		return;
1792f4f8d17SMarek Behún 
180389711b3SMarek Behún 	rwtm->reply = *msg;
181389711b3SMarek Behún 	complete(&rwtm->cmd_done);
182389711b3SMarek Behún }
183389711b3SMarek Behún 
reply_to_mac_addr(u8 * mac,u32 t1,u32 t2)184389711b3SMarek Behún static void reply_to_mac_addr(u8 *mac, u32 t1, u32 t2)
185389711b3SMarek Behún {
186389711b3SMarek Behún 	mac[0] = t1 >> 8;
187389711b3SMarek Behún 	mac[1] = t1;
188389711b3SMarek Behún 	mac[2] = t2 >> 24;
189389711b3SMarek Behún 	mac[3] = t2 >> 16;
190389711b3SMarek Behún 	mac[4] = t2 >> 8;
191389711b3SMarek Behún 	mac[5] = t2;
192389711b3SMarek Behún }
193389711b3SMarek Behún 
mox_get_board_info(struct mox_rwtm * rwtm)194389711b3SMarek Behún static int mox_get_board_info(struct mox_rwtm *rwtm)
195389711b3SMarek Behún {
196389711b3SMarek Behún 	struct armada_37xx_rwtm_tx_msg msg;
197389711b3SMarek Behún 	struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply;
198389711b3SMarek Behún 	int ret;
199389711b3SMarek Behún 
200389711b3SMarek Behún 	msg.command = MBOX_CMD_BOARD_INFO;
201389711b3SMarek Behún 	ret = mbox_send_message(rwtm->mbox, &msg);
202389711b3SMarek Behún 	if (ret < 0)
203389711b3SMarek Behún 		return ret;
204389711b3SMarek Behún 
2056173dd13SMarek Behún 	if (!wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2))
2066173dd13SMarek Behún 		return -ETIMEDOUT;
207389711b3SMarek Behún 
208389711b3SMarek Behún 	ret = mox_get_status(MBOX_CMD_BOARD_INFO, reply->retval);
20972f99888SMarek Behún 	if (ret == -ENODATA) {
210389711b3SMarek Behún 		dev_warn(rwtm->dev,
211389711b3SMarek Behún 			 "Board does not have manufacturing information burned!\n");
21272f99888SMarek Behún 	} else if (ret == -ENOSYS) {
21372f99888SMarek Behún 		dev_notice(rwtm->dev,
21472f99888SMarek Behún 			   "Firmware does not support the BOARD_INFO command\n");
21572f99888SMarek Behún 	} else if (ret < 0) {
21672f99888SMarek Behún 		return ret;
217389711b3SMarek Behún 	} else {
218389711b3SMarek Behún 		rwtm->serial_number = reply->status[1];
219389711b3SMarek Behún 		rwtm->serial_number <<= 32;
220389711b3SMarek Behún 		rwtm->serial_number |= reply->status[0];
221389711b3SMarek Behún 		rwtm->board_version = reply->status[2];
222389711b3SMarek Behún 		rwtm->ram_size = reply->status[3];
223389711b3SMarek Behún 		reply_to_mac_addr(rwtm->mac_address1, reply->status[4],
224389711b3SMarek Behún 				  reply->status[5]);
225389711b3SMarek Behún 		reply_to_mac_addr(rwtm->mac_address2, reply->status[6],
226389711b3SMarek Behún 				  reply->status[7]);
227389711b3SMarek Behún 		rwtm->has_board_info = 1;
228389711b3SMarek Behún 
229389711b3SMarek Behún 		pr_info("Turris Mox serial number %016llX\n",
230389711b3SMarek Behún 			rwtm->serial_number);
231389711b3SMarek Behún 		pr_info("           board version %i\n", rwtm->board_version);
232389711b3SMarek Behún 		pr_info("           burned RAM size %i MiB\n", rwtm->ram_size);
233389711b3SMarek Behún 	}
234389711b3SMarek Behún 
235389711b3SMarek Behún 	msg.command = MBOX_CMD_ECDSA_PUB_KEY;
236389711b3SMarek Behún 	ret = mbox_send_message(rwtm->mbox, &msg);
237389711b3SMarek Behún 	if (ret < 0)
238389711b3SMarek Behún 		return ret;
239389711b3SMarek Behún 
2406173dd13SMarek Behún 	if (!wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2))
2416173dd13SMarek Behún 		return -ETIMEDOUT;
242389711b3SMarek Behún 
243389711b3SMarek Behún 	ret = mox_get_status(MBOX_CMD_ECDSA_PUB_KEY, reply->retval);
24472f99888SMarek Behún 	if (ret == -ENODATA) {
245389711b3SMarek Behún 		dev_warn(rwtm->dev, "Board has no public key burned!\n");
24672f99888SMarek Behún 	} else if (ret == -ENOSYS) {
24772f99888SMarek Behún 		dev_notice(rwtm->dev,
24872f99888SMarek Behún 			   "Firmware does not support the ECDSA_PUB_KEY command\n");
24972f99888SMarek Behún 	} else if (ret < 0) {
25072f99888SMarek Behún 		return ret;
251389711b3SMarek Behún 	} else {
252389711b3SMarek Behún 		u32 *s = reply->status;
253389711b3SMarek Behún 
254389711b3SMarek Behún 		rwtm->has_pubkey = 1;
255389711b3SMarek Behún 		sprintf(rwtm->pubkey,
256389711b3SMarek Behún 			"%06x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
257389711b3SMarek Behún 			ret, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7],
258389711b3SMarek Behún 			s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15]);
259389711b3SMarek Behún 	}
260389711b3SMarek Behún 
261389711b3SMarek Behún 	return 0;
262389711b3SMarek Behún }
263389711b3SMarek Behún 
check_get_random_support(struct mox_rwtm * rwtm)2642eab59cfSPali Rohár static int check_get_random_support(struct mox_rwtm *rwtm)
2652eab59cfSPali Rohár {
2662eab59cfSPali Rohár 	struct armada_37xx_rwtm_tx_msg msg;
2672eab59cfSPali Rohár 	int ret;
2682eab59cfSPali Rohár 
2692eab59cfSPali Rohár 	msg.command = MBOX_CMD_GET_RANDOM;
2702eab59cfSPali Rohár 	msg.args[0] = 1;
2712eab59cfSPali Rohár 	msg.args[1] = rwtm->buf_phys;
2722eab59cfSPali Rohár 	msg.args[2] = 4;
2732eab59cfSPali Rohár 
2742eab59cfSPali Rohár 	ret = mbox_send_message(rwtm->mbox, &msg);
2752eab59cfSPali Rohár 	if (ret < 0)
2762eab59cfSPali Rohár 		return ret;
2772eab59cfSPali Rohár 
2786173dd13SMarek Behún 	if (!wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2))
2796173dd13SMarek Behún 		return -ETIMEDOUT;
2802eab59cfSPali Rohár 
2812eab59cfSPali Rohár 	return mox_get_status(MBOX_CMD_GET_RANDOM, rwtm->reply.retval);
2822eab59cfSPali Rohár }
2832eab59cfSPali Rohár 
mox_hwrng_read(struct hwrng * rng,void * data,size_t max,bool wait)284389711b3SMarek Behún static int mox_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
285389711b3SMarek Behún {
286389711b3SMarek Behún 	struct mox_rwtm *rwtm = (struct mox_rwtm *) rng->priv;
287389711b3SMarek Behún 	struct armada_37xx_rwtm_tx_msg msg;
288389711b3SMarek Behún 	int ret;
289389711b3SMarek Behún 
290389711b3SMarek Behún 	if (max > 4096)
291389711b3SMarek Behún 		max = 4096;
292389711b3SMarek Behún 
293389711b3SMarek Behún 	msg.command = MBOX_CMD_GET_RANDOM;
294389711b3SMarek Behún 	msg.args[0] = 1;
295389711b3SMarek Behún 	msg.args[1] = rwtm->buf_phys;
296389711b3SMarek Behún 	msg.args[2] = (max + 3) & ~3;
297389711b3SMarek Behún 
298389711b3SMarek Behún 	if (!wait) {
299389711b3SMarek Behún 		if (!mutex_trylock(&rwtm->busy))
300389711b3SMarek Behún 			return -EBUSY;
301389711b3SMarek Behún 	} else {
302389711b3SMarek Behún 		mutex_lock(&rwtm->busy);
303389711b3SMarek Behún 	}
304389711b3SMarek Behún 
305389711b3SMarek Behún 	ret = mbox_send_message(rwtm->mbox, &msg);
306389711b3SMarek Behún 	if (ret < 0)
307389711b3SMarek Behún 		goto unlock_mutex;
308389711b3SMarek Behún 
309389711b3SMarek Behún 	ret = wait_for_completion_interruptible(&rwtm->cmd_done);
310389711b3SMarek Behún 	if (ret < 0)
311389711b3SMarek Behún 		goto unlock_mutex;
312389711b3SMarek Behún 
313389711b3SMarek Behún 	ret = mox_get_status(MBOX_CMD_GET_RANDOM, rwtm->reply.retval);
314389711b3SMarek Behún 	if (ret < 0)
315389711b3SMarek Behún 		goto unlock_mutex;
316389711b3SMarek Behún 
317389711b3SMarek Behún 	memcpy(data, rwtm->buf, max);
318389711b3SMarek Behún 	ret = max;
319389711b3SMarek Behún 
320389711b3SMarek Behún unlock_mutex:
321389711b3SMarek Behún 	mutex_unlock(&rwtm->busy);
322389711b3SMarek Behún 	return ret;
323389711b3SMarek Behún }
324389711b3SMarek Behún 
32550524d78SMarek Behún #ifdef CONFIG_DEBUG_FS
rwtm_debug_open(struct inode * inode,struct file * file)32650524d78SMarek Behún static int rwtm_debug_open(struct inode *inode, struct file *file)
32750524d78SMarek Behún {
32850524d78SMarek Behún 	file->private_data = inode->i_private;
32950524d78SMarek Behún 
33050524d78SMarek Behún 	return nonseekable_open(inode, file);
33150524d78SMarek Behún }
33250524d78SMarek Behún 
do_sign_read(struct file * file,char __user * buf,size_t len,loff_t * ppos)33350524d78SMarek Behún static ssize_t do_sign_read(struct file *file, char __user *buf, size_t len,
33450524d78SMarek Behún 			    loff_t *ppos)
33550524d78SMarek Behún {
33650524d78SMarek Behún 	struct mox_rwtm *rwtm = file->private_data;
33750524d78SMarek Behún 	ssize_t ret;
33850524d78SMarek Behún 
33950524d78SMarek Behún 	/* only allow one read, of 136 bytes, from position 0 */
34050524d78SMarek Behún 	if (*ppos != 0)
34150524d78SMarek Behún 		return 0;
34250524d78SMarek Behún 
34350524d78SMarek Behún 	if (len < 136)
34450524d78SMarek Behún 		return -EINVAL;
34550524d78SMarek Behún 
34650524d78SMarek Behún 	if (!rwtm->last_sig_done)
34750524d78SMarek Behún 		return -ENODATA;
34850524d78SMarek Behún 
34950524d78SMarek Behún 	/* 2 arrays of 17 32-bit words are 136 bytes */
35050524d78SMarek Behún 	ret = simple_read_from_buffer(buf, len, ppos, rwtm->last_sig, 136);
35150524d78SMarek Behún 	rwtm->last_sig_done = 0;
35250524d78SMarek Behún 
35350524d78SMarek Behún 	return ret;
35450524d78SMarek Behún }
35550524d78SMarek Behún 
do_sign_write(struct file * file,const char __user * buf,size_t len,loff_t * ppos)35650524d78SMarek Behún static ssize_t do_sign_write(struct file *file, const char __user *buf,
35750524d78SMarek Behún 			     size_t len, loff_t *ppos)
35850524d78SMarek Behún {
35950524d78SMarek Behún 	struct mox_rwtm *rwtm = file->private_data;
36050524d78SMarek Behún 	struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply;
36150524d78SMarek Behún 	struct armada_37xx_rwtm_tx_msg msg;
36250524d78SMarek Behún 	loff_t dummy = 0;
36350524d78SMarek Behún 	ssize_t ret;
36450524d78SMarek Behún 
36550524d78SMarek Behún 	/* the input is a SHA-512 hash, so exactly 64 bytes have to be read */
36650524d78SMarek Behún 	if (len != 64)
36750524d78SMarek Behún 		return -EINVAL;
36850524d78SMarek Behún 
36950524d78SMarek Behún 	/* if last result is not zero user has not read that information yet */
37050524d78SMarek Behún 	if (rwtm->last_sig_done)
37150524d78SMarek Behún 		return -EBUSY;
37250524d78SMarek Behún 
37350524d78SMarek Behún 	if (!mutex_trylock(&rwtm->busy))
37450524d78SMarek Behún 		return -EBUSY;
37550524d78SMarek Behún 
37650524d78SMarek Behún 	/*
37750524d78SMarek Behún 	 * Here we have to send:
37850524d78SMarek Behún 	 *   1. Address of the input to sign.
37950524d78SMarek Behún 	 *      The input is an array of 17 32-bit words, the first (most
38050524d78SMarek Behún 	 *      significat) is 0, the rest 16 words are copied from the SHA-512
38150524d78SMarek Behún 	 *      hash given by the user and converted from BE to LE.
38250524d78SMarek Behún 	 *   2. Address of the buffer where ECDSA signature value R shall be
38350524d78SMarek Behún 	 *      stored by the rWTM firmware.
38450524d78SMarek Behún 	 *   3. Address of the buffer where ECDSA signature value S shall be
38550524d78SMarek Behún 	 *      stored by the rWTM firmware.
38650524d78SMarek Behún 	 */
38750524d78SMarek Behún 	memset(rwtm->buf, 0, 4);
38850524d78SMarek Behún 	ret = simple_write_to_buffer(rwtm->buf + 4, 64, &dummy, buf, len);
38950524d78SMarek Behún 	if (ret < 0)
39050524d78SMarek Behún 		goto unlock_mutex;
39150524d78SMarek Behún 	be32_to_cpu_array(rwtm->buf, rwtm->buf, 17);
39250524d78SMarek Behún 
39350524d78SMarek Behún 	msg.command = MBOX_CMD_SIGN;
39450524d78SMarek Behún 	msg.args[0] = 1;
39550524d78SMarek Behún 	msg.args[1] = rwtm->buf_phys;
39650524d78SMarek Behún 	msg.args[2] = rwtm->buf_phys + 68;
39750524d78SMarek Behún 	msg.args[3] = rwtm->buf_phys + 2 * 68;
39850524d78SMarek Behún 	ret = mbox_send_message(rwtm->mbox, &msg);
39950524d78SMarek Behún 	if (ret < 0)
40050524d78SMarek Behún 		goto unlock_mutex;
40150524d78SMarek Behún 
40250524d78SMarek Behún 	ret = wait_for_completion_interruptible(&rwtm->cmd_done);
40350524d78SMarek Behún 	if (ret < 0)
40450524d78SMarek Behún 		goto unlock_mutex;
40550524d78SMarek Behún 
40650524d78SMarek Behún 	ret = MBOX_STS_VALUE(reply->retval);
40750524d78SMarek Behún 	if (MBOX_STS_ERROR(reply->retval) != MBOX_STS_SUCCESS)
40850524d78SMarek Behún 		goto unlock_mutex;
40950524d78SMarek Behún 
41050524d78SMarek Behún 	/*
41150524d78SMarek Behún 	 * Here we read the R and S values of the ECDSA signature
41250524d78SMarek Behún 	 * computed by the rWTM firmware and convert their words from
41350524d78SMarek Behún 	 * LE to BE.
41450524d78SMarek Behún 	 */
41550524d78SMarek Behún 	memcpy(rwtm->last_sig, rwtm->buf + 68, 136);
41650524d78SMarek Behún 	cpu_to_be32_array(rwtm->last_sig, rwtm->last_sig, 34);
41750524d78SMarek Behún 	rwtm->last_sig_done = 1;
41850524d78SMarek Behún 
41950524d78SMarek Behún 	mutex_unlock(&rwtm->busy);
42050524d78SMarek Behún 	return len;
42150524d78SMarek Behún unlock_mutex:
42250524d78SMarek Behún 	mutex_unlock(&rwtm->busy);
42350524d78SMarek Behún 	return ret;
42450524d78SMarek Behún }
42550524d78SMarek Behún 
42650524d78SMarek Behún static const struct file_operations do_sign_fops = {
42750524d78SMarek Behún 	.owner	= THIS_MODULE,
42850524d78SMarek Behún 	.open	= rwtm_debug_open,
42950524d78SMarek Behún 	.read	= do_sign_read,
43050524d78SMarek Behún 	.write	= do_sign_write,
43150524d78SMarek Behún 	.llseek	= no_llseek,
43250524d78SMarek Behún };
43350524d78SMarek Behún 
rwtm_register_debugfs(struct mox_rwtm * rwtm)43450524d78SMarek Behún static int rwtm_register_debugfs(struct mox_rwtm *rwtm)
43550524d78SMarek Behún {
43650524d78SMarek Behún 	struct dentry *root, *entry;
43750524d78SMarek Behún 
43850524d78SMarek Behún 	root = debugfs_create_dir("turris-mox-rwtm", NULL);
43950524d78SMarek Behún 
44050524d78SMarek Behún 	if (IS_ERR(root))
44150524d78SMarek Behún 		return PTR_ERR(root);
44250524d78SMarek Behún 
44350524d78SMarek Behún 	entry = debugfs_create_file_unsafe("do_sign", 0600, root, rwtm,
44450524d78SMarek Behún 					   &do_sign_fops);
44550524d78SMarek Behún 	if (IS_ERR(entry))
44650524d78SMarek Behún 		goto err_remove;
44750524d78SMarek Behún 
44850524d78SMarek Behún 	rwtm->debugfs_root = root;
44950524d78SMarek Behún 
45050524d78SMarek Behún 	return 0;
45150524d78SMarek Behún err_remove:
45250524d78SMarek Behún 	debugfs_remove_recursive(root);
45350524d78SMarek Behún 	return PTR_ERR(entry);
45450524d78SMarek Behún }
45550524d78SMarek Behún 
rwtm_unregister_debugfs(struct mox_rwtm * rwtm)45650524d78SMarek Behún static void rwtm_unregister_debugfs(struct mox_rwtm *rwtm)
45750524d78SMarek Behún {
45850524d78SMarek Behún 	debugfs_remove_recursive(rwtm->debugfs_root);
45950524d78SMarek Behún }
46050524d78SMarek Behún #else
rwtm_register_debugfs(struct mox_rwtm * rwtm)46150524d78SMarek Behún static inline int rwtm_register_debugfs(struct mox_rwtm *rwtm)
46250524d78SMarek Behún {
46350524d78SMarek Behún 	return 0;
46450524d78SMarek Behún }
46550524d78SMarek Behún 
rwtm_unregister_debugfs(struct mox_rwtm * rwtm)46650524d78SMarek Behún static inline void rwtm_unregister_debugfs(struct mox_rwtm *rwtm)
46750524d78SMarek Behún {
46850524d78SMarek Behún }
46950524d78SMarek Behún #endif
47050524d78SMarek Behún 
turris_mox_rwtm_probe(struct platform_device * pdev)471389711b3SMarek Behún static int turris_mox_rwtm_probe(struct platform_device *pdev)
472389711b3SMarek Behún {
473389711b3SMarek Behún 	struct mox_rwtm *rwtm;
474389711b3SMarek Behún 	struct device *dev = &pdev->dev;
475389711b3SMarek Behún 	int ret;
476389711b3SMarek Behún 
477389711b3SMarek Behún 	rwtm = devm_kzalloc(dev, sizeof(*rwtm), GFP_KERNEL);
478389711b3SMarek Behún 	if (!rwtm)
479389711b3SMarek Behún 		return -ENOMEM;
480389711b3SMarek Behún 
481389711b3SMarek Behún 	rwtm->dev = dev;
482389711b3SMarek Behún 	rwtm->buf = dmam_alloc_coherent(dev, PAGE_SIZE, &rwtm->buf_phys,
483389711b3SMarek Behún 					GFP_KERNEL);
484389711b3SMarek Behún 	if (!rwtm->buf)
485389711b3SMarek Behún 		return -ENOMEM;
486389711b3SMarek Behún 
487389711b3SMarek Behún 	ret = mox_kobj_create(rwtm);
488389711b3SMarek Behún 	if (ret < 0) {
489389711b3SMarek Behún 		dev_err(dev, "Cannot create turris-mox-rwtm kobject!\n");
490389711b3SMarek Behún 		return ret;
491389711b3SMarek Behún 	}
492389711b3SMarek Behún 
493389711b3SMarek Behún 	ret = sysfs_create_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs);
494389711b3SMarek Behún 	if (ret < 0) {
495389711b3SMarek Behún 		dev_err(dev, "Cannot create sysfs files!\n");
496389711b3SMarek Behún 		goto put_kobj;
497389711b3SMarek Behún 	}
498389711b3SMarek Behún 
499389711b3SMarek Behún 	platform_set_drvdata(pdev, rwtm);
500389711b3SMarek Behún 
501389711b3SMarek Behún 	mutex_init(&rwtm->busy);
502*d027ac4aSMarek Behún 	init_completion(&rwtm->cmd_done);
503389711b3SMarek Behún 
504389711b3SMarek Behún 	rwtm->mbox_client.dev = dev;
505389711b3SMarek Behún 	rwtm->mbox_client.rx_callback = mox_rwtm_rx_callback;
506389711b3SMarek Behún 
507389711b3SMarek Behún 	rwtm->mbox = mbox_request_channel(&rwtm->mbox_client, 0);
508389711b3SMarek Behún 	if (IS_ERR(rwtm->mbox)) {
509389711b3SMarek Behún 		ret = PTR_ERR(rwtm->mbox);
510389711b3SMarek Behún 		if (ret != -EPROBE_DEFER)
511389711b3SMarek Behún 			dev_err(dev, "Cannot request mailbox channel: %i\n",
512389711b3SMarek Behún 				ret);
513389711b3SMarek Behún 		goto remove_files;
514389711b3SMarek Behún 	}
515389711b3SMarek Behún 
516389711b3SMarek Behún 	ret = mox_get_board_info(rwtm);
517389711b3SMarek Behún 	if (ret < 0)
518389711b3SMarek Behún 		dev_warn(dev, "Cannot read board information: %i\n", ret);
519389711b3SMarek Behún 
5202eab59cfSPali Rohár 	ret = check_get_random_support(rwtm);
5212eab59cfSPali Rohár 	if (ret < 0) {
5222eab59cfSPali Rohár 		dev_notice(dev,
5232eab59cfSPali Rohár 			   "Firmware does not support the GET_RANDOM command\n");
5242eab59cfSPali Rohár 		goto free_channel;
5252eab59cfSPali Rohár 	}
5262eab59cfSPali Rohár 
527389711b3SMarek Behún 	rwtm->hwrng.name = DRIVER_NAME "_hwrng";
528389711b3SMarek Behún 	rwtm->hwrng.read = mox_hwrng_read;
529389711b3SMarek Behún 	rwtm->hwrng.priv = (unsigned long) rwtm;
530389711b3SMarek Behún 
531389711b3SMarek Behún 	ret = devm_hwrng_register(dev, &rwtm->hwrng);
532389711b3SMarek Behún 	if (ret < 0) {
533389711b3SMarek Behún 		dev_err(dev, "Cannot register HWRNG: %i\n", ret);
534389711b3SMarek Behún 		goto free_channel;
535389711b3SMarek Behún 	}
536389711b3SMarek Behún 
53750524d78SMarek Behún 	ret = rwtm_register_debugfs(rwtm);
53850524d78SMarek Behún 	if (ret < 0) {
53950524d78SMarek Behún 		dev_err(dev, "Failed creating debugfs entries: %i\n", ret);
54050524d78SMarek Behún 		goto free_channel;
54150524d78SMarek Behún 	}
54250524d78SMarek Behún 
543fae20160SPali Rohár 	dev_info(dev, "HWRNG successfully registered\n");
544fae20160SPali Rohár 
545389711b3SMarek Behún 	return 0;
546389711b3SMarek Behún 
547389711b3SMarek Behún free_channel:
548389711b3SMarek Behún 	mbox_free_channel(rwtm->mbox);
549389711b3SMarek Behún remove_files:
550389711b3SMarek Behún 	sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs);
551389711b3SMarek Behún put_kobj:
552389711b3SMarek Behún 	kobject_put(rwtm_to_kobj(rwtm));
553389711b3SMarek Behún 	return ret;
554389711b3SMarek Behún }
555389711b3SMarek Behún 
turris_mox_rwtm_remove(struct platform_device * pdev)556389711b3SMarek Behún static int turris_mox_rwtm_remove(struct platform_device *pdev)
557389711b3SMarek Behún {
558389711b3SMarek Behún 	struct mox_rwtm *rwtm = platform_get_drvdata(pdev);
559389711b3SMarek Behún 
56050524d78SMarek Behún 	rwtm_unregister_debugfs(rwtm);
561389711b3SMarek Behún 	sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs);
562389711b3SMarek Behún 	kobject_put(rwtm_to_kobj(rwtm));
563389711b3SMarek Behún 	mbox_free_channel(rwtm->mbox);
564389711b3SMarek Behún 
565389711b3SMarek Behún 	return 0;
566389711b3SMarek Behún }
567389711b3SMarek Behún 
568389711b3SMarek Behún static const struct of_device_id turris_mox_rwtm_match[] = {
569389711b3SMarek Behún 	{ .compatible = "cznic,turris-mox-rwtm", },
57090ae4721SPali Rohár 	{ .compatible = "marvell,armada-3700-rwtm-firmware", },
571389711b3SMarek Behún 	{ },
572389711b3SMarek Behún };
573389711b3SMarek Behún 
574389711b3SMarek Behún MODULE_DEVICE_TABLE(of, turris_mox_rwtm_match);
575389711b3SMarek Behún 
576389711b3SMarek Behún static struct platform_driver turris_mox_rwtm_driver = {
577389711b3SMarek Behún 	.probe	= turris_mox_rwtm_probe,
578389711b3SMarek Behún 	.remove	= turris_mox_rwtm_remove,
579389711b3SMarek Behún 	.driver	= {
580389711b3SMarek Behún 		.name		= DRIVER_NAME,
581389711b3SMarek Behún 		.of_match_table	= turris_mox_rwtm_match,
582389711b3SMarek Behún 	},
583389711b3SMarek Behún };
584389711b3SMarek Behún module_platform_driver(turris_mox_rwtm_driver);
585389711b3SMarek Behún 
586389711b3SMarek Behún MODULE_LICENSE("GPL v2");
587389711b3SMarek Behún MODULE_DESCRIPTION("Turris Mox rWTM firmware driver");
588b37c3848SMarek Behún MODULE_AUTHOR("Marek Behun <kabel@kernel.org>");
589