1 // SPDX-License-Identifier: GPL-2.0
3 * Turris Mox rWTM firmware driver
5 * Copyright (C) 2019 Marek Behun <marek.behun@nic.cz>
8 #include <linux/armada-37xx-rwtm-mailbox.h>
9 #include <linux/completion.h>
10 #include <linux/dma-mapping.h>
11 #include <linux/hw_random.h>
12 #include <linux/mailbox_client.h>
13 #include <linux/module.h>
14 #include <linux/mutex.h>
16 #include <linux/platform_device.h>
17 #include <linux/slab.h>
19 #define DRIVER_NAME "turris-mox-rwtm"
22 * The macros and constants below come from Turris Mox's rWTM firmware code.
23 * This firmware is open source and it's sources can be found at
24 * https://gitlab.labs.nic.cz/turris/mox-boot-builder/tree/master/wtmi.
27 #define MBOX_STS_SUCCESS (0 << 30)
28 #define MBOX_STS_FAIL (1 << 30)
29 #define MBOX_STS_BADCMD (2 << 30)
30 #define MBOX_STS_ERROR(s) ((s) & (3 << 30))
31 #define MBOX_STS_VALUE(s) (((s) >> 10) & 0xfffff)
32 #define MBOX_STS_CMD(s) ((s) & 0x3ff)
35 MBOX_CMD_GET_RANDOM
= 1,
36 MBOX_CMD_BOARD_INFO
= 2,
37 MBOX_CMD_ECDSA_PUB_KEY
= 3,
42 MBOX_CMD_OTP_READ
= 7,
43 MBOX_CMD_OTP_WRITE
= 8,
50 struct mbox_client mbox_client
;
51 struct mbox_chan
*mbox
;
52 struct mox_kobject
*kobj
;
55 struct armada_37xx_rwtm_rx_msg reply
;
61 struct completion cmd_done
;
63 /* board information */
66 int board_version
, ram_size
;
67 u8 mac_address1
[6], mac_address2
[6];
69 /* public key burned in eFuse */
76 struct mox_rwtm
*rwtm
;
79 static inline struct kobject
*rwtm_to_kobj(struct mox_rwtm
*rwtm
)
81 return &rwtm
->kobj
->kobj
;
84 static inline struct mox_rwtm
*to_rwtm(struct kobject
*kobj
)
86 return container_of(kobj
, struct mox_kobject
, kobj
)->rwtm
;
89 static void mox_kobj_release(struct kobject
*kobj
)
91 kfree(to_rwtm(kobj
)->kobj
);
94 static struct kobj_type mox_kobj_ktype
= {
95 .release
= mox_kobj_release
,
96 .sysfs_ops
= &kobj_sysfs_ops
,
99 static int mox_kobj_create(struct mox_rwtm
*rwtm
)
101 rwtm
->kobj
= kzalloc(sizeof(*rwtm
->kobj
), GFP_KERNEL
);
105 kobject_init(rwtm_to_kobj(rwtm
), &mox_kobj_ktype
);
106 if (kobject_add(rwtm_to_kobj(rwtm
), firmware_kobj
, "turris-mox-rwtm")) {
107 kobject_put(rwtm_to_kobj(rwtm
));
111 rwtm
->kobj
->rwtm
= rwtm
;
116 #define MOX_ATTR_RO(name, format, cat) \
118 name##_show(struct kobject *kobj, struct kobj_attribute *a, \
121 struct mox_rwtm *rwtm = to_rwtm(kobj); \
122 if (!rwtm->has_##cat) \
124 return sprintf(buf, format, rwtm->name); \
126 static struct kobj_attribute mox_attr_##name = __ATTR_RO(name)
128 MOX_ATTR_RO(serial_number
, "%016llX\n", board_info
);
129 MOX_ATTR_RO(board_version
, "%i\n", board_info
);
130 MOX_ATTR_RO(ram_size
, "%i\n", board_info
);
131 MOX_ATTR_RO(mac_address1
, "%pM\n", board_info
);
132 MOX_ATTR_RO(mac_address2
, "%pM\n", board_info
);
133 MOX_ATTR_RO(pubkey
, "%s\n", pubkey
);
135 static int mox_get_status(enum mbox_cmd cmd
, u32 retval
)
137 if (MBOX_STS_CMD(retval
) != cmd
||
138 MBOX_STS_ERROR(retval
) != MBOX_STS_SUCCESS
)
140 else if (MBOX_STS_ERROR(retval
) == MBOX_STS_FAIL
)
141 return -(int)MBOX_STS_VALUE(retval
);
143 return MBOX_STS_VALUE(retval
);
146 static const struct attribute
*mox_rwtm_attrs
[] = {
147 &mox_attr_serial_number
.attr
,
148 &mox_attr_board_version
.attr
,
149 &mox_attr_ram_size
.attr
,
150 &mox_attr_mac_address1
.attr
,
151 &mox_attr_mac_address2
.attr
,
152 &mox_attr_pubkey
.attr
,
156 static void mox_rwtm_rx_callback(struct mbox_client
*cl
, void *data
)
158 struct mox_rwtm
*rwtm
= dev_get_drvdata(cl
->dev
);
159 struct armada_37xx_rwtm_rx_msg
*msg
= data
;
162 complete(&rwtm
->cmd_done
);
165 static void reply_to_mac_addr(u8
*mac
, u32 t1
, u32 t2
)
175 static int mox_get_board_info(struct mox_rwtm
*rwtm
)
177 struct armada_37xx_rwtm_tx_msg msg
;
178 struct armada_37xx_rwtm_rx_msg
*reply
= &rwtm
->reply
;
181 msg
.command
= MBOX_CMD_BOARD_INFO
;
182 ret
= mbox_send_message(rwtm
->mbox
, &msg
);
186 ret
= wait_for_completion_timeout(&rwtm
->cmd_done
, HZ
/ 2);
190 ret
= mox_get_status(MBOX_CMD_BOARD_INFO
, reply
->retval
);
191 if (ret
< 0 && ret
!= -ENODATA
) {
193 } else if (ret
== -ENODATA
) {
195 "Board does not have manufacturing information burned!\n");
197 rwtm
->serial_number
= reply
->status
[1];
198 rwtm
->serial_number
<<= 32;
199 rwtm
->serial_number
|= reply
->status
[0];
200 rwtm
->board_version
= reply
->status
[2];
201 rwtm
->ram_size
= reply
->status
[3];
202 reply_to_mac_addr(rwtm
->mac_address1
, reply
->status
[4],
204 reply_to_mac_addr(rwtm
->mac_address2
, reply
->status
[6],
206 rwtm
->has_board_info
= 1;
208 pr_info("Turris Mox serial number %016llX\n",
209 rwtm
->serial_number
);
210 pr_info(" board version %i\n", rwtm
->board_version
);
211 pr_info(" burned RAM size %i MiB\n", rwtm
->ram_size
);
214 msg
.command
= MBOX_CMD_ECDSA_PUB_KEY
;
215 ret
= mbox_send_message(rwtm
->mbox
, &msg
);
219 ret
= wait_for_completion_timeout(&rwtm
->cmd_done
, HZ
/ 2);
223 ret
= mox_get_status(MBOX_CMD_ECDSA_PUB_KEY
, reply
->retval
);
224 if (ret
< 0 && ret
!= -ENODATA
) {
226 } else if (ret
== -ENODATA
) {
227 dev_warn(rwtm
->dev
, "Board has no public key burned!\n");
229 u32
*s
= reply
->status
;
231 rwtm
->has_pubkey
= 1;
232 sprintf(rwtm
->pubkey
,
233 "%06x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
234 ret
, s
[0], s
[1], s
[2], s
[3], s
[4], s
[5], s
[6], s
[7],
235 s
[8], s
[9], s
[10], s
[11], s
[12], s
[13], s
[14], s
[15]);
241 static int mox_hwrng_read(struct hwrng
*rng
, void *data
, size_t max
, bool wait
)
243 struct mox_rwtm
*rwtm
= (struct mox_rwtm
*) rng
->priv
;
244 struct armada_37xx_rwtm_tx_msg msg
;
250 msg
.command
= MBOX_CMD_GET_RANDOM
;
252 msg
.args
[1] = rwtm
->buf_phys
;
253 msg
.args
[2] = (max
+ 3) & ~3;
256 if (!mutex_trylock(&rwtm
->busy
))
259 mutex_lock(&rwtm
->busy
);
262 ret
= mbox_send_message(rwtm
->mbox
, &msg
);
266 ret
= wait_for_completion_interruptible(&rwtm
->cmd_done
);
270 ret
= mox_get_status(MBOX_CMD_GET_RANDOM
, rwtm
->reply
.retval
);
274 memcpy(data
, rwtm
->buf
, max
);
278 mutex_unlock(&rwtm
->busy
);
282 static int turris_mox_rwtm_probe(struct platform_device
*pdev
)
284 struct mox_rwtm
*rwtm
;
285 struct device
*dev
= &pdev
->dev
;
288 rwtm
= devm_kzalloc(dev
, sizeof(*rwtm
), GFP_KERNEL
);
293 rwtm
->buf
= dmam_alloc_coherent(dev
, PAGE_SIZE
, &rwtm
->buf_phys
,
298 ret
= mox_kobj_create(rwtm
);
300 dev_err(dev
, "Cannot create turris-mox-rwtm kobject!\n");
304 ret
= sysfs_create_files(rwtm_to_kobj(rwtm
), mox_rwtm_attrs
);
306 dev_err(dev
, "Cannot create sysfs files!\n");
310 platform_set_drvdata(pdev
, rwtm
);
312 mutex_init(&rwtm
->busy
);
314 rwtm
->mbox_client
.dev
= dev
;
315 rwtm
->mbox_client
.rx_callback
= mox_rwtm_rx_callback
;
317 rwtm
->mbox
= mbox_request_channel(&rwtm
->mbox_client
, 0);
318 if (IS_ERR(rwtm
->mbox
)) {
319 ret
= PTR_ERR(rwtm
->mbox
);
320 if (ret
!= -EPROBE_DEFER
)
321 dev_err(dev
, "Cannot request mailbox channel: %i\n",
326 init_completion(&rwtm
->cmd_done
);
328 ret
= mox_get_board_info(rwtm
);
330 dev_warn(dev
, "Cannot read board information: %i\n", ret
);
332 rwtm
->hwrng
.name
= DRIVER_NAME
"_hwrng";
333 rwtm
->hwrng
.read
= mox_hwrng_read
;
334 rwtm
->hwrng
.priv
= (unsigned long) rwtm
;
335 rwtm
->hwrng
.quality
= 1024;
337 ret
= devm_hwrng_register(dev
, &rwtm
->hwrng
);
339 dev_err(dev
, "Cannot register HWRNG: %i\n", ret
);
346 mbox_free_channel(rwtm
->mbox
);
348 sysfs_remove_files(rwtm_to_kobj(rwtm
), mox_rwtm_attrs
);
350 kobject_put(rwtm_to_kobj(rwtm
));
354 static int turris_mox_rwtm_remove(struct platform_device
*pdev
)
356 struct mox_rwtm
*rwtm
= platform_get_drvdata(pdev
);
358 sysfs_remove_files(rwtm_to_kobj(rwtm
), mox_rwtm_attrs
);
359 kobject_put(rwtm_to_kobj(rwtm
));
360 mbox_free_channel(rwtm
->mbox
);
365 static const struct of_device_id turris_mox_rwtm_match
[] = {
366 { .compatible
= "cznic,turris-mox-rwtm", },
370 MODULE_DEVICE_TABLE(of
, turris_mox_rwtm_match
);
372 static struct platform_driver turris_mox_rwtm_driver
= {
373 .probe
= turris_mox_rwtm_probe
,
374 .remove
= turris_mox_rwtm_remove
,
377 .of_match_table
= turris_mox_rwtm_match
,
380 module_platform_driver(turris_mox_rwtm_driver
);
382 MODULE_LICENSE("GPL v2");
383 MODULE_DESCRIPTION("Turris Mox rWTM firmware driver");
384 MODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>");