1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * NCI based driver for Samsung S3FWRN5 NFC chip |
4 | * |
5 | * Copyright (C) 2015 Samsung Electrnoics |
6 | * Robert Baldyga <r.baldyga@samsung.com> |
7 | */ |
8 | |
9 | #include <linux/completion.h> |
10 | #include <linux/firmware.h> |
11 | #include <crypto/hash.h> |
12 | #include <crypto/sha1.h> |
13 | |
14 | #include "s3fwrn5.h" |
15 | #include "firmware.h" |
16 | |
17 | struct s3fwrn5_fw_version { |
18 | __u8 major; |
19 | __u8 build1; |
20 | __u8 build2; |
21 | __u8 target; |
22 | }; |
23 | |
24 | static int s3fwrn5_fw_send_msg(struct s3fwrn5_fw_info *fw_info, |
25 | struct sk_buff *msg, struct sk_buff **rsp) |
26 | { |
27 | struct s3fwrn5_info *info = |
28 | container_of(fw_info, struct s3fwrn5_info, fw_info); |
29 | long ret; |
30 | |
31 | reinit_completion(x: &fw_info->completion); |
32 | |
33 | ret = s3fwrn5_write(info, skb: msg); |
34 | if (ret < 0) |
35 | return ret; |
36 | |
37 | ret = wait_for_completion_interruptible_timeout( |
38 | x: &fw_info->completion, timeout: msecs_to_jiffies(m: 1000)); |
39 | if (ret < 0) |
40 | return ret; |
41 | else if (ret == 0) |
42 | return -ENXIO; |
43 | |
44 | if (!fw_info->rsp) |
45 | return -EINVAL; |
46 | |
47 | *rsp = fw_info->rsp; |
48 | fw_info->rsp = NULL; |
49 | |
50 | return 0; |
51 | } |
52 | |
53 | static int s3fwrn5_fw_prep_msg(struct s3fwrn5_fw_info *fw_info, |
54 | struct sk_buff **msg, u8 type, u8 code, const void *data, u16 len) |
55 | { |
56 | struct s3fwrn5_fw_header hdr; |
57 | struct sk_buff *skb; |
58 | |
59 | hdr.type = type | fw_info->parity; |
60 | fw_info->parity ^= 0x80; |
61 | hdr.code = code; |
62 | hdr.len = len; |
63 | |
64 | skb = alloc_skb(S3FWRN5_FW_HDR_SIZE + len, GFP_KERNEL); |
65 | if (!skb) |
66 | return -ENOMEM; |
67 | |
68 | skb_put_data(skb, data: &hdr, S3FWRN5_FW_HDR_SIZE); |
69 | if (len) |
70 | skb_put_data(skb, data, len); |
71 | |
72 | *msg = skb; |
73 | |
74 | return 0; |
75 | } |
76 | |
77 | static int s3fwrn5_fw_get_bootinfo(struct s3fwrn5_fw_info *fw_info, |
78 | struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo) |
79 | { |
80 | struct sk_buff *msg, *rsp = NULL; |
81 | struct s3fwrn5_fw_header *hdr; |
82 | int ret; |
83 | |
84 | /* Send GET_BOOTINFO command */ |
85 | |
86 | ret = s3fwrn5_fw_prep_msg(fw_info, msg: &msg, S3FWRN5_FW_MSG_CMD, |
87 | S3FWRN5_FW_CMD_GET_BOOTINFO, NULL, len: 0); |
88 | if (ret < 0) |
89 | return ret; |
90 | |
91 | ret = s3fwrn5_fw_send_msg(fw_info, msg, rsp: &rsp); |
92 | kfree_skb(skb: msg); |
93 | if (ret < 0) |
94 | return ret; |
95 | |
96 | hdr = (struct s3fwrn5_fw_header *) rsp->data; |
97 | if (hdr->code != S3FWRN5_FW_RET_SUCCESS) { |
98 | ret = -EINVAL; |
99 | goto out; |
100 | } |
101 | |
102 | memcpy(bootinfo, rsp->data + S3FWRN5_FW_HDR_SIZE, 10); |
103 | |
104 | out: |
105 | kfree_skb(skb: rsp); |
106 | return ret; |
107 | } |
108 | |
109 | static int s3fwrn5_fw_enter_update_mode(struct s3fwrn5_fw_info *fw_info, |
110 | const void *hash_data, u16 hash_size, |
111 | const void *sig_data, u16 sig_size) |
112 | { |
113 | struct s3fwrn5_fw_cmd_enter_updatemode args; |
114 | struct sk_buff *msg, *rsp = NULL; |
115 | struct s3fwrn5_fw_header *hdr; |
116 | int ret; |
117 | |
118 | /* Send ENTER_UPDATE_MODE command */ |
119 | |
120 | args.hashcode_size = hash_size; |
121 | args.signature_size = sig_size; |
122 | |
123 | ret = s3fwrn5_fw_prep_msg(fw_info, msg: &msg, S3FWRN5_FW_MSG_CMD, |
124 | S3FWRN5_FW_CMD_ENTER_UPDATE_MODE, data: &args, len: sizeof(args)); |
125 | if (ret < 0) |
126 | return ret; |
127 | |
128 | ret = s3fwrn5_fw_send_msg(fw_info, msg, rsp: &rsp); |
129 | kfree_skb(skb: msg); |
130 | if (ret < 0) |
131 | return ret; |
132 | |
133 | hdr = (struct s3fwrn5_fw_header *) rsp->data; |
134 | if (hdr->code != S3FWRN5_FW_RET_SUCCESS) { |
135 | ret = -EPROTO; |
136 | goto out; |
137 | } |
138 | |
139 | kfree_skb(skb: rsp); |
140 | |
141 | /* Send hashcode data */ |
142 | |
143 | ret = s3fwrn5_fw_prep_msg(fw_info, msg: &msg, S3FWRN5_FW_MSG_DATA, code: 0, |
144 | data: hash_data, len: hash_size); |
145 | if (ret < 0) |
146 | return ret; |
147 | |
148 | ret = s3fwrn5_fw_send_msg(fw_info, msg, rsp: &rsp); |
149 | kfree_skb(skb: msg); |
150 | if (ret < 0) |
151 | return ret; |
152 | |
153 | hdr = (struct s3fwrn5_fw_header *) rsp->data; |
154 | if (hdr->code != S3FWRN5_FW_RET_SUCCESS) { |
155 | ret = -EPROTO; |
156 | goto out; |
157 | } |
158 | |
159 | kfree_skb(skb: rsp); |
160 | |
161 | /* Send signature data */ |
162 | |
163 | ret = s3fwrn5_fw_prep_msg(fw_info, msg: &msg, S3FWRN5_FW_MSG_DATA, code: 0, |
164 | data: sig_data, len: sig_size); |
165 | if (ret < 0) |
166 | return ret; |
167 | |
168 | ret = s3fwrn5_fw_send_msg(fw_info, msg, rsp: &rsp); |
169 | kfree_skb(skb: msg); |
170 | if (ret < 0) |
171 | return ret; |
172 | |
173 | hdr = (struct s3fwrn5_fw_header *) rsp->data; |
174 | if (hdr->code != S3FWRN5_FW_RET_SUCCESS) |
175 | ret = -EPROTO; |
176 | |
177 | out: |
178 | kfree_skb(skb: rsp); |
179 | return ret; |
180 | } |
181 | |
182 | static int s3fwrn5_fw_update_sector(struct s3fwrn5_fw_info *fw_info, |
183 | u32 base_addr, const void *data) |
184 | { |
185 | struct s3fwrn5_fw_cmd_update_sector args; |
186 | struct sk_buff *msg, *rsp = NULL; |
187 | struct s3fwrn5_fw_header *hdr; |
188 | int ret, i; |
189 | |
190 | /* Send UPDATE_SECTOR command */ |
191 | |
192 | args.base_address = base_addr; |
193 | |
194 | ret = s3fwrn5_fw_prep_msg(fw_info, msg: &msg, S3FWRN5_FW_MSG_CMD, |
195 | S3FWRN5_FW_CMD_UPDATE_SECTOR, data: &args, len: sizeof(args)); |
196 | if (ret < 0) |
197 | return ret; |
198 | |
199 | ret = s3fwrn5_fw_send_msg(fw_info, msg, rsp: &rsp); |
200 | kfree_skb(skb: msg); |
201 | if (ret < 0) |
202 | return ret; |
203 | |
204 | hdr = (struct s3fwrn5_fw_header *) rsp->data; |
205 | if (hdr->code != S3FWRN5_FW_RET_SUCCESS) { |
206 | ret = -EPROTO; |
207 | goto err; |
208 | } |
209 | |
210 | kfree_skb(skb: rsp); |
211 | |
212 | /* Send data split into 256-byte packets */ |
213 | |
214 | for (i = 0; i < 16; ++i) { |
215 | ret = s3fwrn5_fw_prep_msg(fw_info, msg: &msg, |
216 | S3FWRN5_FW_MSG_DATA, code: 0, data: data+256*i, len: 256); |
217 | if (ret < 0) |
218 | break; |
219 | |
220 | ret = s3fwrn5_fw_send_msg(fw_info, msg, rsp: &rsp); |
221 | kfree_skb(skb: msg); |
222 | if (ret < 0) |
223 | break; |
224 | |
225 | hdr = (struct s3fwrn5_fw_header *) rsp->data; |
226 | if (hdr->code != S3FWRN5_FW_RET_SUCCESS) { |
227 | ret = -EPROTO; |
228 | goto err; |
229 | } |
230 | |
231 | kfree_skb(skb: rsp); |
232 | } |
233 | |
234 | return ret; |
235 | |
236 | err: |
237 | kfree_skb(skb: rsp); |
238 | return ret; |
239 | } |
240 | |
241 | static int s3fwrn5_fw_complete_update_mode(struct s3fwrn5_fw_info *fw_info) |
242 | { |
243 | struct sk_buff *msg, *rsp = NULL; |
244 | struct s3fwrn5_fw_header *hdr; |
245 | int ret; |
246 | |
247 | /* Send COMPLETE_UPDATE_MODE command */ |
248 | |
249 | ret = s3fwrn5_fw_prep_msg(fw_info, msg: &msg, S3FWRN5_FW_MSG_CMD, |
250 | S3FWRN5_FW_CMD_COMPLETE_UPDATE_MODE, NULL, len: 0); |
251 | if (ret < 0) |
252 | return ret; |
253 | |
254 | ret = s3fwrn5_fw_send_msg(fw_info, msg, rsp: &rsp); |
255 | kfree_skb(skb: msg); |
256 | if (ret < 0) |
257 | return ret; |
258 | |
259 | hdr = (struct s3fwrn5_fw_header *) rsp->data; |
260 | if (hdr->code != S3FWRN5_FW_RET_SUCCESS) |
261 | ret = -EPROTO; |
262 | |
263 | kfree_skb(skb: rsp); |
264 | |
265 | return ret; |
266 | } |
267 | |
268 | /* |
269 | * Firmware header structure: |
270 | * |
271 | * 0x00 - 0x0B : Date and time string (w/o NUL termination) |
272 | * 0x10 - 0x13 : Firmware version |
273 | * 0x14 - 0x17 : Signature address |
274 | * 0x18 - 0x1B : Signature size |
275 | * 0x1C - 0x1F : Firmware image address |
276 | * 0x20 - 0x23 : Firmware sectors count |
277 | * 0x24 - 0x27 : Custom signature address |
278 | * 0x28 - 0x2B : Custom signature size |
279 | */ |
280 | |
281 | #define 44 |
282 | |
283 | int s3fwrn5_fw_request_firmware(struct s3fwrn5_fw_info *fw_info) |
284 | { |
285 | struct s3fwrn5_fw_image *fw = &fw_info->fw; |
286 | u32 sig_off; |
287 | u32 image_off; |
288 | u32 custom_sig_off; |
289 | int ret; |
290 | |
291 | ret = request_firmware(fw: &fw->fw, name: fw_info->fw_name, |
292 | device: &fw_info->ndev->nfc_dev->dev); |
293 | if (ret < 0) |
294 | return ret; |
295 | |
296 | if (fw->fw->size < S3FWRN5_FW_IMAGE_HEADER_SIZE) { |
297 | release_firmware(fw: fw->fw); |
298 | return -EINVAL; |
299 | } |
300 | |
301 | memcpy(fw->date, fw->fw->data + 0x00, 12); |
302 | fw->date[12] = '\0'; |
303 | |
304 | memcpy(&fw->version, fw->fw->data + 0x10, 4); |
305 | |
306 | memcpy(&sig_off, fw->fw->data + 0x14, 4); |
307 | fw->sig = fw->fw->data + sig_off; |
308 | memcpy(&fw->sig_size, fw->fw->data + 0x18, 4); |
309 | |
310 | memcpy(&image_off, fw->fw->data + 0x1C, 4); |
311 | fw->image = fw->fw->data + image_off; |
312 | memcpy(&fw->image_sectors, fw->fw->data + 0x20, 4); |
313 | |
314 | memcpy(&custom_sig_off, fw->fw->data + 0x24, 4); |
315 | fw->custom_sig = fw->fw->data + custom_sig_off; |
316 | memcpy(&fw->custom_sig_size, fw->fw->data + 0x28, 4); |
317 | |
318 | return 0; |
319 | } |
320 | |
321 | static void s3fwrn5_fw_release_firmware(struct s3fwrn5_fw_info *fw_info) |
322 | { |
323 | release_firmware(fw: fw_info->fw.fw); |
324 | } |
325 | |
326 | static int s3fwrn5_fw_get_base_addr( |
327 | struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo, u32 *base_addr) |
328 | { |
329 | int i; |
330 | static const struct { |
331 | u8 version[4]; |
332 | u32 base_addr; |
333 | } match[] = { |
334 | {{0x05, 0x00, 0x00, 0x00}, 0x00005000}, |
335 | {{0x05, 0x00, 0x00, 0x01}, 0x00003000}, |
336 | {{0x05, 0x00, 0x00, 0x02}, 0x00003000}, |
337 | {{0x05, 0x00, 0x00, 0x03}, 0x00003000}, |
338 | {{0x05, 0x00, 0x00, 0x05}, 0x00003000} |
339 | }; |
340 | |
341 | for (i = 0; i < ARRAY_SIZE(match); ++i) |
342 | if (bootinfo->hw_version[0] == match[i].version[0] && |
343 | bootinfo->hw_version[1] == match[i].version[1] && |
344 | bootinfo->hw_version[3] == match[i].version[3]) { |
345 | *base_addr = match[i].base_addr; |
346 | return 0; |
347 | } |
348 | |
349 | return -EINVAL; |
350 | } |
351 | |
352 | static inline bool |
353 | s3fwrn5_fw_is_custom(const struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo) |
354 | { |
355 | return !!bootinfo->hw_version[2]; |
356 | } |
357 | |
358 | int s3fwrn5_fw_setup(struct s3fwrn5_fw_info *fw_info) |
359 | { |
360 | struct device *dev = &fw_info->ndev->nfc_dev->dev; |
361 | struct s3fwrn5_fw_cmd_get_bootinfo_rsp bootinfo; |
362 | int ret; |
363 | |
364 | /* Get bootloader info */ |
365 | |
366 | ret = s3fwrn5_fw_get_bootinfo(fw_info, bootinfo: &bootinfo); |
367 | if (ret < 0) { |
368 | dev_err(dev, "Failed to get bootinfo, ret=%02x\n" , ret); |
369 | goto err; |
370 | } |
371 | |
372 | /* Match hardware version to obtain firmware base address */ |
373 | |
374 | ret = s3fwrn5_fw_get_base_addr(bootinfo: &bootinfo, base_addr: &fw_info->base_addr); |
375 | if (ret < 0) { |
376 | dev_err(dev, "Unknown hardware version\n" ); |
377 | goto err; |
378 | } |
379 | |
380 | fw_info->sector_size = bootinfo.sector_size; |
381 | |
382 | fw_info->sig_size = s3fwrn5_fw_is_custom(bootinfo: &bootinfo) ? |
383 | fw_info->fw.custom_sig_size : fw_info->fw.sig_size; |
384 | fw_info->sig = s3fwrn5_fw_is_custom(bootinfo: &bootinfo) ? |
385 | fw_info->fw.custom_sig : fw_info->fw.sig; |
386 | |
387 | return 0; |
388 | |
389 | err: |
390 | s3fwrn5_fw_release_firmware(fw_info); |
391 | return ret; |
392 | } |
393 | |
394 | bool s3fwrn5_fw_check_version(const struct s3fwrn5_fw_info *fw_info, u32 version) |
395 | { |
396 | struct s3fwrn5_fw_version *new = (void *) &fw_info->fw.version; |
397 | struct s3fwrn5_fw_version *old = (void *) &version; |
398 | |
399 | if (new->major > old->major) |
400 | return true; |
401 | if (new->build1 > old->build1) |
402 | return true; |
403 | if (new->build2 > old->build2) |
404 | return true; |
405 | |
406 | return false; |
407 | } |
408 | |
409 | int s3fwrn5_fw_download(struct s3fwrn5_fw_info *fw_info) |
410 | { |
411 | struct device *dev = &fw_info->ndev->nfc_dev->dev; |
412 | struct s3fwrn5_fw_image *fw = &fw_info->fw; |
413 | u8 hash_data[SHA1_DIGEST_SIZE]; |
414 | struct crypto_shash *tfm; |
415 | u32 image_size, off; |
416 | int ret; |
417 | |
418 | image_size = fw_info->sector_size * fw->image_sectors; |
419 | |
420 | /* Compute SHA of firmware data */ |
421 | |
422 | tfm = crypto_alloc_shash(alg_name: "sha1" , type: 0, mask: 0); |
423 | if (IS_ERR(ptr: tfm)) { |
424 | dev_err(dev, "Cannot allocate shash (code=%pe)\n" , tfm); |
425 | return PTR_ERR(ptr: tfm); |
426 | } |
427 | |
428 | ret = crypto_shash_tfm_digest(tfm, data: fw->image, len: image_size, out: hash_data); |
429 | |
430 | crypto_free_shash(tfm); |
431 | if (ret) { |
432 | dev_err(dev, "Cannot compute hash (code=%d)\n" , ret); |
433 | return ret; |
434 | } |
435 | |
436 | /* Firmware update process */ |
437 | |
438 | dev_info(dev, "Firmware update: %s\n" , fw_info->fw_name); |
439 | |
440 | ret = s3fwrn5_fw_enter_update_mode(fw_info, hash_data, |
441 | SHA1_DIGEST_SIZE, sig_data: fw_info->sig, sig_size: fw_info->sig_size); |
442 | if (ret < 0) { |
443 | dev_err(dev, "Unable to enter update mode\n" ); |
444 | return ret; |
445 | } |
446 | |
447 | for (off = 0; off < image_size; off += fw_info->sector_size) { |
448 | ret = s3fwrn5_fw_update_sector(fw_info, |
449 | base_addr: fw_info->base_addr + off, data: fw->image + off); |
450 | if (ret < 0) { |
451 | dev_err(dev, "Firmware update error (code=%d)\n" , ret); |
452 | return ret; |
453 | } |
454 | } |
455 | |
456 | ret = s3fwrn5_fw_complete_update_mode(fw_info); |
457 | if (ret < 0) { |
458 | dev_err(dev, "Unable to complete update mode\n" ); |
459 | return ret; |
460 | } |
461 | |
462 | dev_info(dev, "Firmware update: success\n" ); |
463 | |
464 | return ret; |
465 | } |
466 | |
467 | void s3fwrn5_fw_init(struct s3fwrn5_fw_info *fw_info, const char *fw_name) |
468 | { |
469 | fw_info->parity = 0x00; |
470 | fw_info->rsp = NULL; |
471 | fw_info->fw.fw = NULL; |
472 | strcpy(p: fw_info->fw_name, q: fw_name); |
473 | init_completion(x: &fw_info->completion); |
474 | } |
475 | |
476 | void s3fwrn5_fw_cleanup(struct s3fwrn5_fw_info *fw_info) |
477 | { |
478 | s3fwrn5_fw_release_firmware(fw_info); |
479 | } |
480 | |
481 | int s3fwrn5_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) |
482 | { |
483 | struct s3fwrn5_info *info = nci_get_drvdata(ndev); |
484 | struct s3fwrn5_fw_info *fw_info = &info->fw_info; |
485 | |
486 | if (WARN_ON(fw_info->rsp)) { |
487 | kfree_skb(skb); |
488 | return -EINVAL; |
489 | } |
490 | |
491 | fw_info->rsp = skb; |
492 | |
493 | complete(&fw_info->completion); |
494 | |
495 | return 0; |
496 | } |
497 | |