1389711b3SMarek Behún // SPDX-License-Identifier: GPL-2.0
2389711b3SMarek Behún /*
3389711b3SMarek Behún * Turris Mox rWTM firmware driver
4389711b3SMarek Behún *
5b37c3848SMarek 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
107389711b3SMarek Behún 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
177389711b3SMarek Behún if (completion_done(&rwtm->cmd_done))
178389711b3SMarek Behún return;
179389711b3SMarek 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
205389711b3SMarek Behún if (!wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2))
206389711b3SMarek Behún return -ETIMEDOUT;
20772f99888SMarek Behún
208389711b3SMarek Behún ret = mox_get_status(MBOX_CMD_BOARD_INFO, reply->retval);
209389711b3SMarek Behún if (ret == -ENODATA) {
21072f99888SMarek Behún dev_warn(rwtm->dev,
21172f99888SMarek 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");
215389711b3SMarek Behún } else if (ret < 0) {
216389711b3SMarek 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
240389711b3SMarek Behún if (!wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2))
241389711b3SMarek Behún return -ETIMEDOUT;
242389711b3SMarek Behún
24372f99888SMarek Behún ret = mox_get_status(MBOX_CMD_ECDSA_PUB_KEY, reply->retval);
244389711b3SMarek Behún if (ret == -ENODATA) {
24572f99888SMarek 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) {
250389711b3SMarek 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 }
2632eab59cfSPali Rohár
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
2782eab59cfSPali Rohár if (!wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2))
2792eab59cfSPali Rohár 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);
502389711b3SMarek 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
520389711b3SMarek Behún 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
5272eab59cfSPali Rohár 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
537389711b3SMarek 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
54350524d78SMarek Behún dev_info(dev, "HWRNG successfully registered\n");
544fae20160SPali Rohár
545fae20160SPali Rohár 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
560389711b3SMarek Behún rwtm_unregister_debugfs(rwtm);
56150524d78SMarek 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", },
570389711b3SMarek Behún { .compatible = "marvell,armada-3700-rwtm-firmware", },
571*90ae4721SPali Rohár { },
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");
588389711b3SMarek Behún MODULE_AUTHOR("Marek Behun <kabel@kernel.org>");
589b37c3848SMarek Behún