1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de> |
4 | * Copyright (C) 2021 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de> |
5 | */ |
6 | |
7 | #define pr_fmt(fmt) "caam blob_gen: " fmt |
8 | |
9 | #include <linux/bitfield.h> |
10 | #include <linux/device.h> |
11 | #include <soc/fsl/caam-blob.h> |
12 | |
13 | #include "compat.h" |
14 | #include "desc_constr.h" |
15 | #include "desc.h" |
16 | #include "error.h" |
17 | #include "intern.h" |
18 | #include "jr.h" |
19 | #include "regs.h" |
20 | |
21 | #define CAAM_BLOB_DESC_BYTES_MAX \ |
22 | /* Command to initialize & stating length of descriptor */ \ |
23 | (CAAM_CMD_SZ + \ |
24 | /* Command to append the key-modifier + key-modifier data */ \ |
25 | CAAM_CMD_SZ + CAAM_BLOB_KEYMOD_LENGTH + \ |
26 | /* Command to include input key + pointer to the input key */ \ |
27 | CAAM_CMD_SZ + CAAM_PTR_SZ_MAX + \ |
28 | /* Command to include output key + pointer to the output key */ \ |
29 | CAAM_CMD_SZ + CAAM_PTR_SZ_MAX + \ |
30 | /* Command describing the operation to perform */ \ |
31 | CAAM_CMD_SZ) |
32 | |
33 | struct caam_blob_priv { |
34 | struct device jrdev; |
35 | }; |
36 | |
37 | struct caam_blob_job_result { |
38 | int err; |
39 | struct completion completion; |
40 | }; |
41 | |
42 | static void caam_blob_job_done(struct device *dev, u32 *desc, u32 err, void *context) |
43 | { |
44 | struct caam_blob_job_result *res = context; |
45 | int ecode = 0; |
46 | |
47 | dev_dbg(dev, "%s %d: err 0x%x\n" , __func__, __LINE__, err); |
48 | |
49 | if (err) |
50 | ecode = caam_jr_strstatus(dev, err); |
51 | |
52 | res->err = ecode; |
53 | |
54 | /* |
55 | * Upon completion, desc points to a buffer containing a CAAM job |
56 | * descriptor which encapsulates data into an externally-storable |
57 | * blob. |
58 | */ |
59 | complete(&res->completion); |
60 | } |
61 | |
62 | int caam_process_blob(struct caam_blob_priv *priv, |
63 | struct caam_blob_info *info, bool encap) |
64 | { |
65 | const struct caam_drv_private *ctrlpriv; |
66 | struct caam_blob_job_result testres; |
67 | struct device *jrdev = &priv->jrdev; |
68 | dma_addr_t dma_in, dma_out; |
69 | int op = OP_PCLID_BLOB; |
70 | size_t output_len; |
71 | u32 *desc; |
72 | u32 moo; |
73 | int ret; |
74 | |
75 | if (info->key_mod_len > CAAM_BLOB_KEYMOD_LENGTH) |
76 | return -EINVAL; |
77 | |
78 | if (encap) { |
79 | op |= OP_TYPE_ENCAP_PROTOCOL; |
80 | output_len = info->input_len + CAAM_BLOB_OVERHEAD; |
81 | } else { |
82 | op |= OP_TYPE_DECAP_PROTOCOL; |
83 | output_len = info->input_len - CAAM_BLOB_OVERHEAD; |
84 | } |
85 | |
86 | desc = kzalloc(CAAM_BLOB_DESC_BYTES_MAX, GFP_KERNEL); |
87 | if (!desc) |
88 | return -ENOMEM; |
89 | |
90 | dma_in = dma_map_single(jrdev, info->input, info->input_len, |
91 | DMA_TO_DEVICE); |
92 | if (dma_mapping_error(dev: jrdev, dma_addr: dma_in)) { |
93 | dev_err(jrdev, "unable to map input DMA buffer\n" ); |
94 | ret = -ENOMEM; |
95 | goto out_free; |
96 | } |
97 | |
98 | dma_out = dma_map_single(jrdev, info->output, output_len, |
99 | DMA_FROM_DEVICE); |
100 | if (dma_mapping_error(dev: jrdev, dma_addr: dma_out)) { |
101 | dev_err(jrdev, "unable to map output DMA buffer\n" ); |
102 | ret = -ENOMEM; |
103 | goto out_unmap_in; |
104 | } |
105 | |
106 | ctrlpriv = dev_get_drvdata(dev: jrdev->parent); |
107 | moo = FIELD_GET(CSTA_MOO, rd_reg32(&ctrlpriv->ctrl->perfmon.status)); |
108 | if (moo != CSTA_MOO_SECURE && moo != CSTA_MOO_TRUSTED) |
109 | dev_warn(jrdev, |
110 | "using insecure test key, enable HAB to use unique device key!\n" ); |
111 | |
112 | /* |
113 | * A data blob is encrypted using a blob key (BK); a random number. |
114 | * The BK is used as an AES-CCM key. The initial block (B0) and the |
115 | * initial counter (Ctr0) are generated automatically and stored in |
116 | * Class 1 Context DWords 0+1+2+3. The random BK is stored in the |
117 | * Class 1 Key Register. Operation Mode is set to AES-CCM. |
118 | */ |
119 | |
120 | init_job_desc(desc, options: 0); |
121 | append_key_as_imm(desc, data: info->key_mod, data_len: info->key_mod_len, |
122 | len: info->key_mod_len, CLASS_2 | KEY_DEST_CLASS_REG); |
123 | append_seq_in_ptr_intlen(desc, ptr: dma_in, len: info->input_len, options: 0); |
124 | append_seq_out_ptr_intlen(desc, ptr: dma_out, len: output_len, options: 0); |
125 | append_operation(desc, options: op); |
126 | |
127 | print_hex_dump_debug("data@" __stringify(__LINE__)": " , |
128 | DUMP_PREFIX_ADDRESS, 16, 1, info->input, |
129 | info->input_len, false); |
130 | print_hex_dump_debug("jobdesc@" __stringify(__LINE__)": " , |
131 | DUMP_PREFIX_ADDRESS, 16, 1, desc, |
132 | desc_bytes(desc), false); |
133 | |
134 | testres.err = 0; |
135 | init_completion(x: &testres.completion); |
136 | |
137 | ret = caam_jr_enqueue(dev: jrdev, desc, cbk: caam_blob_job_done, areq: &testres); |
138 | if (ret == -EINPROGRESS) { |
139 | wait_for_completion(&testres.completion); |
140 | ret = testres.err; |
141 | print_hex_dump_debug("output@" __stringify(__LINE__)": " , |
142 | DUMP_PREFIX_ADDRESS, 16, 1, info->output, |
143 | output_len, false); |
144 | } |
145 | |
146 | if (ret == 0) |
147 | info->output_len = output_len; |
148 | |
149 | dma_unmap_single(jrdev, dma_out, output_len, DMA_FROM_DEVICE); |
150 | out_unmap_in: |
151 | dma_unmap_single(jrdev, dma_in, info->input_len, DMA_TO_DEVICE); |
152 | out_free: |
153 | kfree(objp: desc); |
154 | |
155 | return ret; |
156 | } |
157 | EXPORT_SYMBOL(caam_process_blob); |
158 | |
159 | struct caam_blob_priv *caam_blob_gen_init(void) |
160 | { |
161 | struct caam_drv_private *ctrlpriv; |
162 | struct device *jrdev; |
163 | |
164 | /* |
165 | * caam_blob_gen_init() may expectedly fail with -ENODEV, e.g. when |
166 | * CAAM driver didn't probe or when SoC lacks BLOB support. An |
167 | * error would be harsh in this case, so we stick to info level. |
168 | */ |
169 | |
170 | jrdev = caam_jr_alloc(); |
171 | if (IS_ERR(ptr: jrdev)) { |
172 | pr_info("job ring requested, but none currently available\n" ); |
173 | return ERR_PTR(error: -ENODEV); |
174 | } |
175 | |
176 | ctrlpriv = dev_get_drvdata(dev: jrdev->parent); |
177 | if (!ctrlpriv->blob_present) { |
178 | dev_info(jrdev, "no hardware blob generation support\n" ); |
179 | caam_jr_free(rdev: jrdev); |
180 | return ERR_PTR(error: -ENODEV); |
181 | } |
182 | |
183 | return container_of(jrdev, struct caam_blob_priv, jrdev); |
184 | } |
185 | EXPORT_SYMBOL(caam_blob_gen_init); |
186 | |
187 | void caam_blob_gen_exit(struct caam_blob_priv *priv) |
188 | { |
189 | caam_jr_free(rdev: &priv->jrdev); |
190 | } |
191 | EXPORT_SYMBOL(caam_blob_gen_exit); |
192 | |