This repository has been archived by the owner on Jul 6, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
/
0025-scsi-ufs-connect-to-RPMB-subsystem.patch
343 lines (331 loc) · 9.36 KB
/
0025-scsi-ufs-connect-to-RPMB-subsystem.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tomas Winkler <[email protected]>
Date: Thu, 5 Nov 2015 10:22:44 +0200
Subject: [PATCH] scsi: ufs: connect to RPMB subsystem
Register UFS RPMB LUN with the RPMB subsystem and provide
implementation for the RPMB access operations. RPMB partition is
accessed via a sequence of security protocol in and security protocol
out commands with UFS specific parameters. This multi step process is
abstracted into 4 basic RPMB commands.
V2: resend
V3: resend
V4: Kconfig: use select RPMB to ensure valid configuration
V5: Revamp code using new sequence command.
V6: Resend
V7: Resend
V8: 1. Replace scsi_execute_req_flags() with scsi_execute_req()
2. line over 80 characters fixes
3. scsi_device_get return value has to be checked
V9: V1. adjust to new unregister api
V2: nframes is 0 based now
Change-Id: Ia45c6776d534fb311b6016aaa88f441d403cb0ca
Signed-off-by: Alexander Usyskin <[email protected]>
Signed-off-by: Tomas Winkler <[email protected]>
Tested-by: Avri Altman <[email protected]>
---
drivers/scsi/ufs/Kconfig | 1 +
drivers/scsi/ufs/ufshcd.c | 223 ++++++++++++++++++++++++++++++++++++++
drivers/scsi/ufs/ufshcd.h | 2 +
3 files changed, 226 insertions(+)
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
index e09fe6ab3572..a3c1982b213a 100644
--- a/drivers/scsi/ufs/Kconfig
+++ b/drivers/scsi/ufs/Kconfig
@@ -38,6 +38,7 @@ config SCSI_UFSHCD
select PM_DEVFREQ
select DEVFREQ_GOV_SIMPLE_ONDEMAND
select NLS
+ select RPMB
---help---
This selects the support for UFS devices in Linux, say Y and make
sure that you know the name of your UFS host adapter (the card
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 626f4c3d3c46..b1c7dd8afccf 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -37,11 +37,14 @@
* license terms, and distributes only under these terms.
*/
+#include <asm/unaligned.h>
#include <linux/async.h>
#include <linux/devfreq.h>
#include <linux/nls.h>
#include <linux/of.h>
#include <linux/bitfield.h>
+#include <linux/rpmb.h>
+
#include "ufshcd.h"
#include "ufs_quirks.h"
#include "unipro.h"
@@ -6265,6 +6268,217 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba)
kfree(desc_buf);
}
+#define SEC_PROTOCOL_UFS 0xEC
+#define SEC_SPECIFIC_UFS_RPMB 0x001
+
+#define SEC_PROTOCOL_CMD_SIZE 12
+#define SEC_PROTOCOL_RETRIES 3
+#define SEC_PROTOCOL_RETRIES_ON_RESET 10
+#define SEC_PROTOCOL_TIMEOUT msecs_to_jiffies(1000)
+
+static int
+ufshcd_rpmb_security_out(struct scsi_device *sdev, u8 region,
+ void *frames, u32 trans_len)
+{
+ struct scsi_sense_hdr sshdr;
+ int reset_retries = SEC_PROTOCOL_RETRIES_ON_RESET;
+ int ret;
+ u8 cmd[SEC_PROTOCOL_CMD_SIZE];
+
+ memset(cmd, 0, SEC_PROTOCOL_CMD_SIZE);
+ cmd[0] = SECURITY_PROTOCOL_OUT;
+ cmd[1] = SEC_PROTOCOL_UFS;
+ cmd[2] = region;
+ cmd[3] = SEC_SPECIFIC_UFS_RPMB;
+ cmd[4] = 0; /* inc_512 bit 7 set to 0 */
+ put_unaligned_be32(trans_len, cmd + 6); /* transfer length */
+
+retry:
+ ret = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE,
+ frames, trans_len, &sshdr,
+ SEC_PROTOCOL_TIMEOUT, SEC_PROTOCOL_RETRIES,
+ NULL);
+
+ if (ret && scsi_sense_valid(&sshdr) &&
+ sshdr.sense_key == UNIT_ATTENTION &&
+ sshdr.asc == 0x29 && sshdr.ascq == 0x00)
+ /*
+ * Device reset might occur several times,
+ * give it one more chance
+ */
+ if (--reset_retries > 0)
+ goto retry;
+
+ if (ret)
+ dev_err(&sdev->sdev_gendev, "%s: failed with err %0x\n",
+ __func__, ret);
+
+ if (driver_byte(ret) & DRIVER_SENSE)
+ scsi_print_sense_hdr(sdev, "rpmb: security out", &sshdr);
+
+ return ret;
+}
+
+static int
+ufshcd_rpmb_security_in(struct scsi_device *sdev, u8 region,
+ void *frames, u32 alloc_len)
+{
+ struct scsi_sense_hdr sshdr;
+ int reset_retries = SEC_PROTOCOL_RETRIES_ON_RESET;
+ int ret;
+ u8 cmd[SEC_PROTOCOL_CMD_SIZE];
+
+ memset(cmd, 0, SEC_PROTOCOL_CMD_SIZE);
+ cmd[0] = SECURITY_PROTOCOL_IN;
+ cmd[1] = SEC_PROTOCOL_UFS;
+ cmd[2] = region;
+ cmd[3] = SEC_SPECIFIC_UFS_RPMB;
+ cmd[4] = 0; /* inc_512 bit 7 set to 0 */
+ put_unaligned_be32(alloc_len, cmd + 6); /* allocation length */
+
+retry:
+ ret = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE,
+ frames, alloc_len, &sshdr,
+ SEC_PROTOCOL_TIMEOUT, SEC_PROTOCOL_RETRIES,
+ NULL);
+
+ if (ret && scsi_sense_valid(&sshdr) &&
+ sshdr.sense_key == UNIT_ATTENTION &&
+ sshdr.asc == 0x29 && sshdr.ascq == 0x00)
+ /*
+ * Device reset might occur several times,
+ * give it one more chance
+ */
+ if (--reset_retries > 0)
+ goto retry;
+
+ if (ret)
+ dev_err(&sdev->sdev_gendev, "%s: failed with err %0x\n",
+ __func__, ret);
+
+ if (driver_byte(ret) & DRIVER_SENSE)
+ scsi_print_sense_hdr(sdev, "rpmb: security in", &sshdr);
+
+ return ret;
+}
+
+static int ufshcd_rpmb_cmd_seq(struct device *dev, u8 target,
+ struct rpmb_cmd *cmds, u32 ncmds)
+{
+ unsigned long flags;
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ struct scsi_device *sdev;
+ struct rpmb_cmd *cmd;
+ u32 len;
+ u32 i;
+ int ret;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ sdev = hba->sdev_ufs_rpmb;
+ if (sdev) {
+ ret = scsi_device_get(sdev);
+ if (!ret && !scsi_device_online(sdev)) {
+ ret = -ENODEV;
+ scsi_device_put(sdev);
+ }
+ } else {
+ ret = -ENODEV;
+ }
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ if (ret)
+ return ret;
+
+ for (ret = 0, i = 0; i < ncmds && !ret; i++) {
+ cmd = &cmds[i];
+ len = rpmb_ioc_frames_len_jdec(cmd->nframes);
+ if (cmd->flags & RPMB_F_WRITE)
+ ret = ufshcd_rpmb_security_out(sdev, target,
+ cmd->frames, len);
+ else
+ ret = ufshcd_rpmb_security_in(sdev, target,
+ cmd->frames, len);
+ }
+ scsi_device_put(sdev);
+ return ret;
+}
+
+static int ufshcd_rpmb_get_capacity(struct device *dev, u8 target)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ __be64 block_count;
+ int ret;
+
+ ret = ufshcd_read_unit_desc_param(hba,
+ UFS_UPIU_RPMB_WLUN,
+ UNIT_DESC_PARAM_LOGICAL_BLK_COUNT,
+ (u8 *)&block_count,
+ sizeof(block_count));
+ if (ret)
+ return ret;
+
+ return be64_to_cpu(block_count) * SZ_512 / SZ_128K;
+}
+
+static struct rpmb_ops ufshcd_rpmb_dev_ops = {
+ .cmd_seq = ufshcd_rpmb_cmd_seq,
+ .get_capacity = ufshcd_rpmb_get_capacity,
+ .type = RPMB_TYPE_UFS,
+ .auth_method = RPMB_HMAC_ALGO_SHA_256,
+
+};
+
+static inline void ufshcd_rpmb_add(struct ufs_hba *hba)
+{
+ struct rpmb_dev *rdev;
+ u8 rpmb_rw_size = 1;
+ int ret;
+
+ ret = scsi_device_get(hba->sdev_ufs_rpmb);
+ if (ret)
+ goto out_put_dev;
+
+ if (hba->ufs_version >= UFSHCI_VERSION_21) {
+ ret = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_GEOMETRY, 0,
+ GEOMETRY_DESC_PARAM_RPMB_RW_SIZE,
+ &rpmb_rw_size,
+ sizeof(rpmb_rw_size));
+ if (ret)
+ goto out_put_dev;
+ }
+
+ ufshcd_rpmb_dev_ops.rd_cnt_max = rpmb_rw_size;
+ ufshcd_rpmb_dev_ops.wr_cnt_max = rpmb_rw_size;
+
+ rdev = rpmb_dev_register(hba->dev, 0, &ufshcd_rpmb_dev_ops);
+ if (IS_ERR(rdev)) {
+ dev_warn(hba->dev, "%s: cannot register to rpmb %ld\n",
+ dev_name(hba->dev), PTR_ERR(rdev));
+ goto out_put_dev;
+ }
+
+ return;
+
+out_put_dev:
+ scsi_device_put(hba->sdev_ufs_rpmb);
+ hba->sdev_ufs_rpmb = NULL;
+}
+
+static inline void ufshcd_rpmb_remove(struct ufs_hba *hba)
+{
+ unsigned long flags;
+
+ if (!hba->sdev_ufs_rpmb)
+ return;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+
+ rpmb_dev_unregister_by_device(hba->dev, 0);
+ scsi_device_put(hba->sdev_ufs_rpmb);
+ hba->sdev_ufs_rpmb = NULL;
+
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+}
+
/**
* ufshcd_scsi_add_wlus - Adds required W-LUs
* @hba: per-adapter instance
@@ -6312,6 +6526,8 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
ret = PTR_ERR(sdev_rpmb);
goto remove_sdev_ufs_device;
}
+ hba->sdev_ufs_rpmb = sdev_rpmb;
+
scsi_device_put(sdev_rpmb);
sdev_boot = __scsi_add_device(hba->host, 0, 0,
@@ -6731,6 +6947,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
if (ret)
goto out;
+ ufshcd_rpmb_add(hba);
+
/* Initialize devfreq after UFS device is detected */
if (ufshcd_is_clkscaling_supported(hba)) {
memcpy(&hba->clk_scaling.saved_pwr_info.info,
@@ -7963,6 +8181,8 @@ int ufshcd_shutdown(struct ufs_hba *hba)
pm_runtime_get_sync(hba->dev);
+ ufshcd_rpmb_remove(hba);
+
ret = ufshcd_suspend(hba, UFS_SHUTDOWN_PM);
out:
if (ret)
@@ -7979,7 +8199,10 @@ EXPORT_SYMBOL(ufshcd_shutdown);
*/
void ufshcd_remove(struct ufs_hba *hba)
{
+ ufshcd_rpmb_remove(hba);
+
ufs_sysfs_remove_nodes(hba->dev);
+
scsi_remove_host(hba->host);
/* disable interrupts */
ufshcd_disable_intr(hba, hba->intr_mask);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 34b7d3546bdc..47d51f711ae5 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -458,6 +458,7 @@ struct ufs_stats {
* @utmrdl_dma_addr: UTMRDL DMA address
* @host: Scsi_Host instance of the driver
* @dev: device handle
+ * @sdev_ufs_rpmb: reference to RPMB device W-LU
* @lrb: local reference block
* @lrb_in_use: lrb in use
* @outstanding_tasks: Bits representing outstanding task requests
@@ -523,6 +524,7 @@ struct ufs_hba {
* "UFS device" W-LU.
*/
struct scsi_device *sdev_ufs_device;
+ struct scsi_device *sdev_ufs_rpmb;
enum ufs_dev_pwr_mode curr_dev_pwr_mode;
enum uic_link_state uic_link_state;
--
https://clearlinux.org