Bug Summary

File:.build-ci/../plugins/sed/sedopal_cmd.c
Warning:line 586, column 21
Access to field 'features' results in a dereference of a null pointer (loaded from variable 'ld')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name sedopal_cmd.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/lib/llvm-19/lib/clang/19 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -std=gnu99 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-06-24-175442-590-1 -x c ../plugins/sed/sedopal_cmd.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2
3#include <ctype.h>
4#include <stdio.h>
5#include <stdint.h>
6#include <stdbool.h>
7#include <string.h>
8#include <unistd.h>
9#include <errno(*__errno_location ()).h>
10#include <sys/types.h>
11#include <sys/ioctl.h>
12#include <sys/mount.h>
13#include <linux1/sed-opal.h>
14
15#include <libnvme.h>
16
17#include "sedopal_spec.h"
18#include "sedopal_cmd.h"
19
20/*
21 * ask user for key rather than obtaining it from kernel keyring
22 */
23bool_Bool sedopal_ask_key;
24
25/*
26 * initiate dialog to ask for and confirm new password
27 */
28bool_Bool sedopal_ask_new_key;
29
30/*
31 * perform a destructive drive revert
32 */
33bool_Bool sedopal_destructive_revert;
34
35/*
36 * perform a PSID drive revert
37 */
38bool_Bool sedopal_psid_revert;
39
40/*
41 * Lock read-only
42 */
43bool_Bool sedopal_lock_ro;
44
45/*
46 * Verbose discovery
47 */
48bool_Bool sedopal_discovery_verbose;
49
50/*
51 * discovery with udev output
52 */
53bool_Bool sedopal_discovery_udev;
54
55/*
56 * level 0 discovery buffer
57 */
58char level0_discovery_buf[4096];
59
60struct sedopal_feature_parser {
61 uint32_t features;
62 void *tper_desc;
63 void *locking_desc;
64 void *geometry_reporting_desc;
65 void *opalv1_desc;
66 void *single_user_mode_desc;
67 void *datastore_desc;
68 void *opalv2_desc;
69 void *opalite_desc;
70 void *pyrite_v1_desc;
71 void *pyrite_v2_desc;
72 void *ruby_desc;
73 void *locking_lba_desc;
74 void *block_sid_auth_desc;
75 void *config_ns_desc;
76 void *data_removal_desc;
77 void *ns_geometry_desc;
78};
79
80/*
81 * Map method status codes to error text
82 */
83static const char * const sedopal_errors[] = {
84 [SED_STATUS_SUCCESS] = "Success",
85 [SED_STATUS_NOT_AUTHORIZED] = "Host Not Authorized",
86 [SED_STATUS_OBSOLETE_1] = "Obsolete",
87 [SED_STATUS_SP_BUSY] = "SP Session Busy",
88 [SED_STATUS_SP_FAILED] = "SP Failed",
89 [SED_STATUS_SP_DISABLED] = "SP Disabled",
90 [SED_STATUS_SP_FROZEN] = "SP Frozen",
91 [SED_STATUS_NO_SESSIONS_AVAILABLE] = "No Sessions Available",
92 [SED_STATUS_UNIQUENESS_CONFLICT] = "Uniqueness Conflict",
93 [SED_STATUS_INSUFFICIENT_SPACE] = "Insufficient Space",
94 [SED_STATUS_INSUFFICIENT_ROWS] = "Insufficient Rows",
95 [SED_STATUS_OBSOLETE_2] = "Obsolete",
96 [SED_STATUS_INVALID_PARAMETER] = "Invalid Parameter",
97 [SED_STATUS_OBSOLETE_3] = "Obsolete",
98 [SED_STATUS_OBSOLETE_4] = "Obsolete",
99 [SED_STATUS_TPER_MALFUNCTION] = "TPER Malfunction",
100 [SED_STATUS_TRANSACTION_FAILURE] = "Transaction Failure",
101 [SED_STATUS_RESPONSE_OVERFLOW] = "Response Overflow",
102 [SED_STATUS_AUTHORITY_LOCKED_OUT] = "Authority Locked Out",
103};
104
105const char *sedopal_error_to_text(int code)
106{
107 if (code == SED_STATUS_FAIL)
108 return "Failed";
109
110 if (code == SED_STATUS_NO_METHOD_STATUS)
111 return "Method returned no status";
112
113 if (code < SED_STATUS_SUCCESS ||
114 code > SED_STATUS_AUTHORITY_LOCKED_OUT)
115 return("Unknown Error");
116
117 return sedopal_errors[code];
118}
119
120/*
121 * Read a user entered password and do some basic validity checks.
122 */
123char *sedopal_get_password(char *prompt)
124{
125 char *pass;
126 int len;
127
128 pass = getpass(prompt);
129 if (pass == NULL((void*)0))
130 return NULL((void*)0);
131
132 len = strlen(pass);
133 if (len < SEDOPAL_MIN_PASSWORD_LEN8) {
134 fprintf(stderrstderr, "Error: password is not long enough\n");
135 return NULL((void*)0);
136 }
137
138 if (len > SEDOPAL_MAX_PASSWORD_LEN32) {
139 fprintf(stderrstderr, "Error: password is too long\n");
140 return NULL((void*)0);
141 }
142
143 return pass;
144}
145
146/*
147 * Initialize a SED Opal key. The key can either specify that the actual
148 * key should be looked up in the kernel keyring, or it should be
149 * populated in the key by prompting the user.
150 */
151int sedopal_set_key(struct opal_key *key)
152{
153#if !NVME_HAVE_KEY_TYPE1
154 /*
155 * If key_type isn't avaialable, force key prompt
156 */
157 sedopal_ask_key = true1;
158#endif
159
160 if (sedopal_ask_key) {
161 char *pass;
162 char *prompt;
163
164 /*
165 * set proper prompt
166 */
167 if (sedopal_ask_new_key)
168 prompt = SEDOPAL_NEW_PW_PROMPT"New Password: ";
169 else {
170 if (sedopal_psid_revert)
171 prompt = SEDOPAL_PSID_PROMPT"PSID: ";
172 else
173 prompt = SEDOPAL_CURRENT_PW_PROMPT"Password: ";
174 }
175
176 pass = sedopal_get_password(prompt);
177 if (pass == NULL((void*)0))
178 return -EINVAL22;
179
180#if NVME_HAVE_KEY_TYPE1
181 key->key_type = OPAL_INCLUDED;
182#endif
183 key->key_len = strlen(pass);
184 memcpy(key->key, pass, key->key_len + 1);
185
186 /*
187 * If getting a new key, ask for it to be re-entered
188 * and verify the two entries are the same.
189 */
190 if (sedopal_ask_new_key) {
191 pass = sedopal_get_password(SEDOPAL_REENTER_PW_PROMPT"Re-enter New Password: ");
192 if (strncmp((char *)key->key, pass, key->key_len)) {
193 fprintf(stderrstderr,
194 "Error: passwords don't match\n");
195 return -EINVAL22;
196 }
197 }
198 } else {
199#if NVME_HAVE_KEY_TYPE1
200 key->key_type = OPAL_KEYRING;
201#endif
202 key->key_len = 0;
203 }
204
205 key->lr = 0;
206
207 return 0;
208}
209
210/*
211 * Prepare a drive for SED Opal locking.
212 */
213int sedopal_cmd_initialize(int fd)
214{
215 int rc;
216 struct opal_key key;
217 struct opal_lr_act lr_act = {};
218 struct opal_user_lr_setup lr_setup = {};
219 struct opal_new_pw new_pw = {};
220 uint8_t locking_state;
221
222 locking_state = sedopal_locking_state(fd);
223
224 if (locking_state & OPAL_FEATURE_LOCKING_ENABLED0x02) {
225 fprintf(stderrstderr,
226 "Error: cannot initialize an initialized drive\n");
227 return -EOPNOTSUPP95;
228 }
229
230 sedopal_ask_key = true1;
231 sedopal_ask_new_key = true1;
232 rc = sedopal_set_key(&key);
233 if (rc != 0)
234 return rc;
235
236 /*
237 * take ownership of the device
238 */
239 rc = ioctl(fd, IOC_OPAL_TAKE_OWNERSHIP(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((222)) << 0) | ((((sizeof(struct opal_key)))) <<
((0 +8)+8)))
, &key);
240 if (rc != 0) {
241 fprintf(stderrstderr,
242 "Error: failed to take device ownership - %d\n", rc);
243 return rc;
244 }
245
246 /*
247 * activate lsp
248 */
249 lr_act.num_lrs = 1;
250 lr_act.sum = false0;
251 lr_act.key = key;
252
253 rc = ioctl(fd, IOC_OPAL_ACTIVATE_LSP(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((223)) << 0) | ((((sizeof(struct opal_lr_act)))) <<
((0 +8)+8)))
, &lr_act);
254 if (rc != 0) {
255 fprintf(stderrstderr, "Error: failed to activate LSP - %d\n", rc);
256 return rc;
257 }
258
259 /*
260 * setup global locking range
261 */
262 lr_setup.range_start = 0;
263 lr_setup.range_length = 0;
264 lr_setup.RLE = true1;
265 if (!sedopal_lock_ro)
266 lr_setup.WLE = true1;
267
268 lr_setup.session.opal_key = key;
269 lr_setup.session.sum = 0;
270 lr_setup.session.who = OPAL_ADMIN1;
271
272 rc = ioctl(fd, IOC_OPAL_LR_SETUP(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((227)) << 0) | ((((sizeof(struct opal_user_lr_setup)
))) << ((0 +8)+8)))
, &lr_setup);
273 if (rc != 0) {
274 fprintf(stderrstderr,
275 "Error: failed to setup locking range - %d\n", rc);
276 return rc;
277 }
278
279 /*
280 * set password
281 */
282 new_pw.new_user_pw.who = OPAL_ADMIN1;
283 new_pw.new_user_pw.opal_key.lr = 0;
284 new_pw.session.who = OPAL_ADMIN1;
285 new_pw.session.sum = 0;
286 new_pw.session.opal_key.lr = 0;
287 new_pw.session.opal_key = key;
288 new_pw.new_user_pw.opal_key = key;
289
290 rc = ioctl(fd, IOC_OPAL_SET_PW(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((224)) << 0) | ((((sizeof(struct opal_new_pw)))) <<
((0 +8)+8)))
, &new_pw);
291 if (rc != 0)
292 fprintf(stderrstderr, "Error: failed setting password - %d\n", rc);
293
294 return rc;
295}
296
297/*
298 * Lock a SED Opal drive
299 */
300int sedopal_cmd_lock(int fd)
301{
302 int lock_state = OPAL_LK;
303
304 if (sedopal_lock_ro)
305 lock_state = OPAL_RO;
306
307 return sedopal_lock_unlock(fd, lock_state);
308}
309
310/*
311 * Unlock a SED Opal drive
312 */
313int sedopal_cmd_unlock(int fd)
314{
315 int rc;
316 int lock_state = OPAL_RW;
317
318 if (sedopal_lock_ro)
319 lock_state = OPAL_RO;
320
321 rc = sedopal_lock_unlock(fd, lock_state);
322
323 /*
324 * If the unlock was successful, force a re-read of the
325 * partition table. Return rc of unlock operation.
326 */
327 if (rc == 0) {
328 if (ioctl(fd, BLKRRPART(((0U) << (((0 +8)+8)+14)) | (((0x12)) << (0 +8))
| (((95)) << 0) | ((0) << ((0 +8)+8)))
, 0) != 0)
329 fprintf(stderrstderr,
330 "Warning: failed re-reading partition\n");
331 }
332
333 return rc;
334}
335
336/*
337 * Prepare and issue an ioctl to lock/unlock a drive
338 */
339int sedopal_lock_unlock(int fd, int lock_state)
340{
341 int rc;
342 struct opal_lock_unlock opal_lu = {};
343 uint8_t locking_state;
344
345 locking_state = sedopal_locking_state(fd);
346
347 if (!(locking_state & OPAL_FEATURE_LOCKING_ENABLED0x02)) {
348 fprintf(stderrstderr,
349 "Error: cannot lock/unlock an uninitialized drive\n");
350 return -EOPNOTSUPP95;
351 }
352
353 rc = sedopal_set_key(&opal_lu.session.opal_key);
354 if (rc != 0)
355 return rc;
356
357 opal_lu.session.sum = 0;
358 opal_lu.session.who = OPAL_ADMIN1;
359 opal_lu.l_state = lock_state;
360
361 rc = ioctl(fd, IOC_OPAL_LOCK_UNLOCK(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((221)) << 0) | ((((sizeof(struct opal_lock_unlock)))
) << ((0 +8)+8)))
, &opal_lu);
362 if (rc != 0)
363 fprintf(stderrstderr,
364 "Error: failed locking or unlocking - %d\n", rc);
365 return rc;
366}
367
368/*
369 * Confirm a destructive drive so that data is inadvertently erased
370 */
371static bool_Bool sedopal_confirm_revert(void)
372{
373 int rc;
374 char ans;
375 bool_Bool confirmed = false0;
376
377 /*
378 * verify that destructive revert is really the intention
379 */
380 fprintf(stdoutstdout,
381 "Destructive revert erases drive data. Continue (y/n)? ");
382 rc = fscanf(stdinstdin, " %c", &ans);
383 if ((rc == 1) && (ans == 'y' || ans == 'Y')) {
384 fprintf(stdoutstdout, "Are you sure (y/n)? ");
385 rc = fscanf(stdinstdin, " %c", &ans);
386 if ((rc == 1) && (ans == 'y' || ans == 'Y'))
387 confirmed = true1;
388 }
389
390 return confirmed;
391}
392
393/*
394 * perform a destructive drive revert
395 */
396static int sedopal_revert_destructive(int fd)
397{
398 struct opal_key key;
399 int rc;
400
401 if (!sedopal_confirm_revert()) {
402 fprintf(stderrstderr, "Aborting destructive revert\n");
403 return -1;
404 }
405
406 /*
407 * for destructive revert, require that key is provided
408 */
409 sedopal_ask_key = true1;
410
411 rc = sedopal_set_key(&key);
412 if (rc == 0)
413 rc = ioctl(fd, IOC_OPAL_REVERT_TPR(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((226)) << 0) | ((((sizeof(struct opal_key)))) <<
((0 +8)+8)))
, &key);
414
415 return rc;
416}
417
418/*
419 * perform a PSID drive revert
420 */
421static int sedopal_revert_psid(int fd)
422{
423#ifdef IOC_OPAL_PSID_REVERT_TPR(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((232)) << 0) | ((((sizeof(struct opal_key)))) <<
((0 +8)+8)))
424 struct opal_key key;
425 int rc;
426
427 if (!sedopal_confirm_revert()) {
428 fprintf(stderrstderr, "Aborting PSID revert\n");
429 return -1;
430 }
431
432 rc = sedopal_set_key(&key);
433 if (rc == 0) {
434 rc = ioctl(fd, IOC_OPAL_PSID_REVERT_TPR(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((232)) << 0) | ((((sizeof(struct opal_key)))) <<
((0 +8)+8)))
, &key);
435 if (rc != 0) {
436 if (rc == EPERM1)
437 fprintf(stderrstderr, "Error: incorrect password\n");
438 else
439 fprintf(stderrstderr, "PSID_REVERT_TPR rc %d\n", rc);
440 }
441 }
442
443 return rc;
444#else
445 fprintf(stderrstderr, "ERROR : PSID revert is not supported\n");
446 return -EOPNOTSUPP95;
447#endif /* IOC_OPAL_PSID_REVERT_TPR */
448}
449
450/*
451 * revert a drive from the provisioned state to a state where locking
452 * is disabled.
453 */
454int sedopal_cmd_revert(int fd)
455{
456 int rc;
457
458 /*
459 * for revert, require that key/PSID is provided
460 */
461 sedopal_ask_key = true1;
462
463 if (sedopal_psid_revert) {
464 rc = sedopal_revert_psid(fd);
465 } else if (sedopal_destructive_revert) {
466 rc = sedopal_revert_destructive(fd);
467 } else {
468#ifdef IOC_OPAL_REVERT_LSP(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((240)) << 0) | ((((sizeof(struct opal_revert_lsp))))
<< ((0 +8)+8)))
469 struct opal_revert_lsp revert_lsp;
470 uint8_t locking_state;
471 char *revert = "LSP";
472
473 locking_state = sedopal_locking_state(fd);
474
475 if (!(locking_state & OPAL_FEATURE_LOCKING_ENABLED0x02)) {
476 fprintf(stderrstderr,
477 "Error: can't revert an uninitialized drive\n");
478 return -EOPNOTSUPP95;
479 }
480
481 if (locking_state & OPAL_FEATURE_LOCKED0x04) {
482 fprintf(stderrstderr,
483 "Error: cannot revert drive while locked\n");
484 return -EOPNOTSUPP95;
485 }
486
487 rc = sedopal_set_key(&revert_lsp.key);
488 if (rc != 0)
489 return rc;
490
491 revert_lsp.options = OPAL_PRESERVE;
492 revert_lsp.__pad = 0;
493
494 rc = ioctl(fd, IOC_OPAL_REVERT_LSP(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((240)) << 0) | ((((sizeof(struct opal_revert_lsp))))
<< ((0 +8)+8)))
, &revert_lsp);
495 if (rc == 0) {
496 revert = "TPER";
497 /*
498 * TPER must also be reverted.
499 */
500 rc = ioctl(fd, IOC_OPAL_REVERT_TPR(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((226)) << 0) | ((((sizeof(struct opal_key)))) <<
((0 +8)+8)))
, &revert_lsp.key);
501 if (rc != 0)
502 fprintf(stderrstderr, "Error: revert TPR - %d\n", rc);
503 }
504
505 if (rc != 0) {
506 if (rc == EPERM1)
507 fprintf(stderrstderr, "Error: incorrect password\n");
508 else
509 fprintf(stderrstderr, "Error: revert %s - %d\n",
510 revert, rc);
511 }
512#else
513 rc = -EOPNOTSUPP95;
514#endif
515 }
516
517 if ((rc != 0) && (rc != EPERM1))
518 fprintf(stderrstderr, "Error: failed reverting drive - %d\n", rc);
519
520 return rc;
521}
522
523/*
524 * Change the password of a drive. The existing password must be
525 * provided and the new password is confirmed by re-entry.
526 */
527int sedopal_cmd_password(int fd)
528{
529 int rc;
530 struct opal_new_pw new_pw = {};
531
532 new_pw.new_user_pw.who = OPAL_ADMIN1;
533 new_pw.new_user_pw.opal_key.lr = 0;
534 new_pw.session.who = OPAL_ADMIN1;
535 new_pw.session.sum = 0;
536 new_pw.session.opal_key.lr = 0;
537
538 /*
539 * get current key
540 */
541 sedopal_ask_key = true1;
542 if (sedopal_set_key(&new_pw.session.opal_key) != 0)
543 return -EINVAL22;
544
545 /*
546 * get new key
547 */
548 sedopal_ask_new_key = true1;
549 if (sedopal_set_key(&new_pw.new_user_pw.opal_key) != 0)
550 return -EINVAL22;
551
552 /*
553 * set admin1 password
554 */
555 rc = ioctl(fd, IOC_OPAL_SET_PW(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((224)) << 0) | ((((sizeof(struct opal_new_pw)))) <<
((0 +8)+8)))
, &new_pw);
556 if (rc != 0) {
557 if (rc == EPERM1)
558 fprintf(stderrstderr, "Error: incorrect password\n");
559 else
560 fprintf(stderrstderr, "Error: setting password - %d\n", rc);
561 return rc;
562 }
563
564#ifdef IOC_OPAL_SET_SID_PW
565 /*
566 * set sid password
567 */
568 rc = ioctl(fd, IOC_OPAL_SET_SID_PW, &new_pw);
569 if (rc != 0) {
570 if (rc == EPERM1)
571 fprintf(stderrstderr, "Error: incorrect password\n");
572 else
573 fprintf(stderrstderr, "Error: setting SID pw - %d\n", rc);
574 }
575#endif
576
577 return rc;
578}
579
580/*
581 * Print the state of locking features.
582 */
583void sedopal_print_locking_features(void *data)
584{
585 struct locking_desc *ld = (struct locking_desc *)data;
14
'ld' initialized to a null pointer value
586 uint8_t features = ld->features;
15
Access to field 'features' results in a dereference of a null pointer (loaded from variable 'ld')
587
588 if (!sedopal_discovery_udev) {
589 printf("Locking Features:\n");
590 printf("\tLocking Supported : %s\n",
591 (features & OPAL_FEATURE_LOCKING_SUPPORTED0x01) ?
592 "yes" : "no");
593 printf("\tLocking Feature Enabled : %s\n",
594 (features & OPAL_FEATURE_LOCKING_ENABLED0x02) ?
595 "yes" : "no");
596 printf("\tLocked : %s\n",
597 (features & OPAL_FEATURE_LOCKED0x04) ? "yes" : "no");
598 printf("\tMedia Encryption : %s\n",
599 (features & OPAL_FEATURE_MEDIA_ENCRYPT0x08) ? "yes" : "no");
600 printf("\tMBR Enabled : %s\n",
601 (features & OPAL_FEATURE_MBR_ENABLED0x10) ? "yes" : "no");
602 printf("\tMBR Done : %s\n",
603 (features & OPAL_FEATURE_MBR_DONE0x20) ? "yes" : "no");
604 } else {
605 printf("DEV_SED_LOCKED=%s\n",
606 (features & OPAL_FEATURE_LOCKING_ENABLED0x02) ?
607 "ENABLED" : "DISABLED");
608 printf("DEV_SED_LOCKING=%s\n",
609 (features & OPAL_FEATURE_LOCKING_ENABLED0x02) ?
610 "ENABLED" : "DISABLED");
611 printf("DEV_SED_LOCKING_SUPP=%s\n",
612 (features & OPAL_FEATURE_LOCKING_SUPPORTED0x01) ?
613 "ENABLED" : "DISABLED");
614 printf("DEV_SED_LOCKING_LOCKED=%s\n",
615 (features & OPAL_FEATURE_LOCKED0x04) ?
616 "ENABLED" : "DISABLED");
617 }
618}
619
620/*
621 * Print the TPer feature.
622 */
623void sedopal_print_tper(void *data)
624{
625 struct tper_desc *td = (struct tper_desc *)data;
626
627 printf("\nSED TPER:\n");
628 printf("\tSync Supported : %s\n",
629 (td->feature & TPER_FEATURE_SYNC0x0001) ? "yes" : "no");
630 printf("\tAsync Supported : %s\n",
631 (td->feature & TPER_FEATURE_ASYNC0x0002) ? "yes" : "no");
632 printf("\tACK/NAK Supported : %s\n",
633 (td->feature & TPER_FEATURE_ACKNAK0x0004) ? "yes" : "no");
634 printf("\tBuffer Management Supported : %s\n",
635 (td->feature & TPER_FEATURE_BUF_MGMT0x0008) ? "yes" : "no");
636 printf("\tStreaming Supported : %s\n",
637 (td->feature & TPER_FEATURE_STREAMING0x0010) ? "yes" : "no");
638 printf("\tComID Management Supported : %s\n",
639 (td->feature & TPER_FEATURE_COMID_MGMT0x0040) ? "yes" : "no");
640}
641
642/*
643 * Print the Geometry feature.
644 */
645void sedopal_print_geometry(void *data)
646{
647 struct geometry_reporting_desc *gd;
648
649 gd = (struct geometry_reporting_desc *)data;
650
651 printf("\nSED Geometry:\n");
652 printf("\tAlignment Required : %s\n",
653 (gd->align & GEOMETRY_ALIGNMENT_REQUIRED0x01) ? "yes" : "no");
654 printf("\tLogical Block Size : %u\n",
655 be32toh(gd->logical_block_size)__bswap_32 (gd->logical_block_size));
656 printf("\tAlignment Granularity : %llx\n",
657 (unsigned long long)(be64toh(gd->alignment_granularity)__bswap_64 (gd->alignment_granularity)));
658 printf("\tLowest Aligned LBA : %llx\n",
659 (unsigned long long)(be64toh(gd->lowest_aligned_lba)__bswap_64 (gd->lowest_aligned_lba)));
660}
661
662/*
663 * Print the opal v1 feature.
664 */
665void sedopal_print_opal_v1(void *data)
666{
667 struct opalv1_desc *v1d = (struct opalv1_desc *)data;
668
669 printf("\nSED OPAL V1.0:\n");
670 printf("\tBase Comid : %d\n",
671 be16toh(v1d->base_comid)__bswap_16 (v1d->base_comid));
672 printf("\tNumber of Comids : %d\n",
673 be16toh(v1d->num_comids)__bswap_16 (v1d->num_comids));
674}
675
676/*
677 * Print the opal v2 feature.
678 */
679void sedopal_print_opal_v2(void *data)
680{
681 struct opalv2_desc *v2d = (struct opalv2_desc *)data;
682
683 printf("\nSED OPAL V2.0:\n");
684 printf("\tRange Crossing : %d\n",
685 !(v2d->flags & OPAL_V2_RANGE_CROSSING0x01));
686 printf("\tBase Comid : %d\n",
687 be16toh(v2d->base_comid)__bswap_16 (v2d->base_comid));
688 printf("\tNumber of Comids : %d\n",
689 be16toh(v2d->num_comids)__bswap_16 (v2d->num_comids));
690 printf("\tNumber of Admin Authorities : %d\n",
691 be16toh(v2d->num_locking_sp_admin_auth)__bswap_16 (v2d->num_locking_sp_admin_auth));
692 printf("\tNumber of User Authorities : %d\n",
693 be16toh(v2d->num_locking_sp_user_auth)__bswap_16 (v2d->num_locking_sp_user_auth));
694 printf("\tInit pin : %d\n",
695 v2d->initial_cpin_sid_ind);
696 printf("\tRevert pin : %d\n",
697 v2d->initial_cpin_sid_revert);
698}
699
700/*
701 * Print the ruby feature.
702 */
703void sedopal_print_ruby(void *data)
704{
705 struct ruby_desc *rd = (struct ruby_desc *)data;
706
707 printf("\nRuby:\n");
708 printf("\tRange Crossing : %d\n",
709 !(rd->flags & RUBY_RANGE_CROSSING0x01));
710 printf("\tBase Comid : %d\n",
711 be16toh(rd->base_comid)__bswap_16 (rd->base_comid));
712 printf("\tNumber of Comids : %d\n",
713 be16toh(rd->num_comids)__bswap_16 (rd->num_comids));
714 printf("\tNumber of Admin Authorities : %d\n",
715 be16toh(rd->num_locking_sp_admin_auth)__bswap_16 (rd->num_locking_sp_admin_auth));
716 printf("\tNumber of User Authorities : %d\n",
717 be16toh(rd->num_locking_sp_user_auth)__bswap_16 (rd->num_locking_sp_user_auth));
718 printf("\tInit pin : %d\n",
719 rd->initial_cpin_sid_ind);
720 printf("\tRevert pin : %d\n",
721 rd->initial_cpin_sid_revert);
722}
723
724/*
725 * Print the opalite feature.
726 */
727void sedopal_print_opalite(void *data)
728{
729 struct opalite_desc *old = (struct opalite_desc *)data;
730
731 printf("\nSED Opalite:\n");
732 printf("\tBase Comid : %d\n",
733 be16toh(old->base_comid)__bswap_16 (old->base_comid));
734 printf("\tNumber of Comids : %d\n",
735 be16toh(old->num_comids)__bswap_16 (old->num_comids));
736 printf("\tInit pin : %d\n",
737 old->initial_cpin_sid_ind);
738 printf("\tRevert pin : %d\n",
739 old->initial_cpin_sid_revert);
740}
741
742/*
743 * Print the pyrite v1 feature.
744 */
745void sedopal_print_pyrite_v1(void *data)
746{
747 struct pyrite_v1_desc *p1d = (struct pyrite_v1_desc *)data;
748
749 printf("\nPyrite V1:\n");
750 printf("\tBase Comid : %d\n",
751 be16toh(p1d->base_comid)__bswap_16 (p1d->base_comid));
752 printf("\tNumber of Comids : %d\n",
753 be16toh(p1d->num_comids)__bswap_16 (p1d->num_comids));
754 printf("\tInit pin : %d\n",
755 p1d->initial_cpin_sid_ind);
756 printf("\tRevert pin : %d\n",
757 p1d->initial_cpin_sid_revert);
758}
759
760/*
761 * Print the pyrite v2 feature.
762 */
763void sedopal_print_pyrite_v2(void *data)
764{
765 struct pyrite_v2_desc *p2d = (struct pyrite_v2_desc *)data;
766
767 printf("\nPyrite V2:\n");
768 printf("\tBase Comid : %d\n",
769 be16toh(p2d->base_comid)__bswap_16 (p2d->base_comid));
770 printf("\tNumber of Comids : %d\n",
771 be16toh(p2d->num_comids)__bswap_16 (p2d->num_comids));
772 printf("\tInit pin : %d\n",
773 p2d->initial_cpin_sid_ind);
774 printf("\tRevert pin : %d\n",
775 p2d->initial_cpin_sid_revert);
776}
777
778/*
779 * Print the single user mode feature.
780 */
781void sedopal_print_sum(void *data)
782{
783 struct single_user_mode_desc *sumd;
784
785 sumd = (struct single_user_mode_desc *)data;
786
787 printf("\nSingle User Mode (SUM):\n");
788 printf("\tNumber of Locking Objects : %u\n",
789 be32toh(sumd->num_locking_objects)__bswap_32 (sumd->num_locking_objects));
790 printf("\tAny Locking Objects in SUM? : %s\n",
791 (sumd->flags & SUM_FEATURE_ANY0x0001) ? "yes" : "no");
792 printf("\tAll Locking Objects in SUM? : %s\n",
793 (sumd->flags & SUM_FEATURE_ALL0x0002) ? "yes" : "no");
794 printf("\tUser Controls Locking Range : %s\n",
795 (sumd->flags & SUM_FEATURE_POLICY0x0004) ? "no" : "yes");
796}
797
798/*
799 * Print the data store table feature.
800 */
801void sedopal_print_datastore(void *data)
802{
803 struct datastore_desc *dsd = (struct datastore_desc *)data;
804
805 printf("\nData Store Table:\n");
806 printf("\tNumber of Tables Supported : %u\n",
807 be16toh(dsd->max_tables)__bswap_16 (dsd->max_tables));
808 printf("\tMax Size of Tables : %u\n",
809 be32toh(dsd->max_table_size)__bswap_32 (dsd->max_table_size));
810 printf("\tTable Size Alignment : %u\n",
811 be32toh(dsd->table_alignment)__bswap_32 (dsd->table_alignment));
812}
813
814/*
815 * Print the block SID authentication feature.
816 */
817void sedopal_print_sid_auth(void *data)
818{
819 struct block_sid_auth_desc *sid_auth_d;
820
821 sid_auth_d = (struct block_sid_auth_desc *)data;
822
823 printf("\nSED Block SID Authentication:\n");
824 printf("\tSID value equal MSID : %s\n",
825 (sid_auth_d->states & BLOCK_SID_VALUE_STATE0x0001) ? "no" : "yes");
826 printf("\tSID auth blocked : %s\n",
827 (sid_auth_d->states & BLOCK_SID_BLOCKED_STATE0x0002) ? "yes" : "no");
828 printf("\tHW reset selected : %s\n",
829 (sid_auth_d->hw_reset & BLOCK_SID_HW_RESET0x0001) ? "yes" : "no");
830}
831
832/*
833 * Print the Locking LBA Ranges Control feature
834 */
835void sedopal_print_locking_lba(void *data)
836{
837 /*
838 * There currently isn't any definition of the level 0 content
839 * of this feature, so defer any printing.
840 */
841}
842
843/*
844 * Print the configurable namespace locking feature.
845 */
846void sedopal_print_config_ns(void *data)
847{
848 struct config_ns_desc *nsd = (struct config_ns_desc *)data;
849
850 printf("\nSED Configurable Namespace Locking:\n");
851 printf("\tNon-global Locking Support : %s\n",
852 (nsd->flags & CONFIG_NS_RANGE_C0x0080) ? "yes" : "no");
853 printf("\tNon-global Lock objects exist : %s\n",
854 (nsd->flags & CONFIG_NS_RANGE_P0x0040) ? "yes" : "no");
855 printf("\tMaximum Key Count : %d\n",
856 be32toh(nsd->max_key_count)__bswap_32 (nsd->max_key_count));
857 printf("\tUnused Key Count : %d\n",
858 be32toh(nsd->unused_key_count)__bswap_32 (nsd->unused_key_count));
859}
860
861/*
862 * Print the data removal mechanism feature.
863 */
864void sedopal_print_data_removal(void *data)
865{
866 struct data_removal_desc *drd = (struct data_removal_desc *)data;
867
868 printf("\nSED Data Removal Mechanism:\n");
869 printf("\tRemoval Operation Processing : %s\n",
870 (drd->flags & DATA_REMOVAL_OPER_PROCESSING0x01) ? "yes" : "no");
871 printf("\tRemoval Operation Interrupted : %s\n",
872 (drd->flags & DATA_REMOVAL_OPER_INTERRUPTED0x02) ? "yes" : "no");
873 printf("\tData Removal Mechanism : %x\n",
874 drd->removal_mechanism);
875 printf("\tData Removal Format : %x\n",
876 drd->format);
877 printf("\tData Removal Time (Bit 0) : %x\n",
878 be16toh(drd->time_mechanism_bit0)__bswap_16 (drd->time_mechanism_bit0));
879 printf("\tData Removal Time (Bit 1) : %x\n",
880 be16toh(drd->time_mechanism_bit1)__bswap_16 (drd->time_mechanism_bit1));
881 printf("\tData Removal Time (Bit 2) : %x\n",
882 be16toh(drd->time_mechanism_bit2)__bswap_16 (drd->time_mechanism_bit2));
883 printf("\tData Removal Time (Bit 5) : %x\n",
884 be16toh(drd->time_mechanism_bit5)__bswap_16 (drd->time_mechanism_bit5));
885}
886
887/*
888 * Print the namespace geometry feature.
889 */
890void sedopal_print_ns_geometry(void *data)
891{
892 struct ns_geometry_desc *nsgd = (struct ns_geometry_desc *)data;
893
894 printf("\nSED Namespace Geometry:\n");
895 printf("\tAlignment Required : %s\n",
896 (nsgd->align & NS_GEOMETRY_ALIGNMENT_REQUIRED0x01) ? "yes" : "no");
897 printf("\tLogical Block Size : %x\n",
898 be32toh(nsgd->logical_block_size)__bswap_32 (nsgd->logical_block_size));
899 printf("\tAlignment Granularity : %llx\n",
900 (unsigned long long)(be64toh(nsgd->alignment_granularity)__bswap_64 (nsgd->alignment_granularity)));
901 printf("\tLowest Aligned LBA : %llx\n",
902 (unsigned long long)(be64toh(nsgd->lowest_aligned_lba)__bswap_64 (nsgd->lowest_aligned_lba)));
903}
904
905void sedopal_parse_features(struct level_0_discovery_features *feat,
906 struct sedopal_feature_parser *sfp)
907{
908 uint16_t code = be16toh(feat->code)__bswap_16 (feat->code);
909
910 switch (code) {
5
Control jumps to 'case 772:' at line 951
911 case OPAL_FEATURE_CODE_LOCKING0x0002:
912 sfp->features |= OPAL_FEATURE_LOCKING0x0002;
913 sfp->locking_desc = (void *)(feat + 1);
914 break;
915 case OPAL_FEATURE_CODE_OPALV10x0200:
916 sfp->features |= OPAL_FEATURE_OPALV10x0008;
917 sfp->opalv1_desc = (void *)(feat + 1);
918 break;
919 case OPAL_FEATURE_CODE_OPALV20x0203:
920 sfp->features |= OPAL_FEATURE_OPALV20x0040;
921 sfp->opalv2_desc = (void *)(feat + 1);
922 break;
923 case OPAL_FEATURE_CODE_TPER0x0001:
924 sfp->features |= OPAL_FEATURE_TPER0x0001;
925 sfp->tper_desc = (void *)(feat + 1);
926 break;
927 case OPAL_FEATURE_CODE_GEOMETRY0x0003:
928 sfp->features |= OPAL_FEATURE_GEOMETRY0x0004;
929 sfp->geometry_reporting_desc = (void *)(feat + 1);
930 break;
931 case OPAL_FEATURE_CODE_SINGLE_USER_MODE0x0201:
932 sfp->features |= OPAL_FEATURE_SINGLE_USER_MODE0x0010;
933 sfp->single_user_mode_desc = (void *)(feat + 1);
934 break;
935 case OPAL_FEATURE_CODE_DATA_STORE0x0202:
936 sfp->features |= OPAL_FEATURE_DATA_STORE0x0020;
937 sfp->datastore_desc = (void *)(feat + 1);
938 break;
939 case OPAL_FEATURE_CODE_OPALITE0x0301:
940 sfp->features |= OPAL_FEATURE_OPALITE0x0080;
941 sfp->opalite_desc = (void *)(feat + 1);
942 break;
943 case OPAL_FEATURE_CODE_PYRITE_V10x0302:
944 sfp->features |= OPAL_FEATURE_PYRITE_V10x0100;
945 sfp->pyrite_v1_desc = (void *)(feat + 1);
946 break;
947 case OPAL_FEATURE_CODE_PYRITE_V20x0303:
948 sfp->features |= OPAL_FEATURE_PYRITE_V20x0200;
949 sfp->pyrite_v2_desc = (void *)(feat + 1);
950 break;
951 case OPAL_FEATURE_CODE_RUBY0x0304:
952 sfp->features |= OPAL_FEATURE_RUBY0x0400;
953 sfp->ruby_desc = (void *)(feat + 1);
954 break;
6
Execution continues on line 953
955 case OPAL_FEATURE_CODE_LOCKING_LBA0x0401:
956 sfp->features |= OPAL_FEATURE_LOCKING_LBA0x0800;
957 sfp->locking_lba_desc = (void *)(feat + 1);
958 break;
959 case OPAL_FEATURE_CODE_BLOCK_SID_AUTH0x0402:
960 sfp->features |= OPAL_FEATURE_BLOCK_SID_AUTH0x1000;
961 sfp->block_sid_auth_desc = (void *)(feat + 1);
962 break;
963 case OPAL_FEATURE_CODE_CONFIG_NS_LOCKING0x0403:
964 sfp->features |= OPAL_FEATURE_CONFIG_NS_LOCKING0x2000;
965 sfp->config_ns_desc = (void *)(feat + 1);
966 break;
967 case OPAL_FEATURE_CODE_DATA_REMOVAL0x0404:
968 sfp->features |= OPAL_FEATURE_DATA_REMOVAL0x4000;
969 sfp->data_removal_desc = (void *)(feat + 1);
970 break;
971 case OPAL_FEATURE_CODE_NS_GEOMETRY0x0405:
972 sfp->features |= OPAL_FEATURE_NS_GEOMETRY0x8000;
973 sfp->ns_geometry_desc = (void *)(feat + 1);
974 break;
975
976 default:
977 break;
978 }
979}
7
Returning without writing to 'sfp->locking_desc'
980
981void sedopal_print_features(struct sedopal_feature_parser *sfp)
982{
983 if (sfp->features & OPAL_FEATURE_OPALV10x0008)
984 sedopal_print_opal_v1(sfp->opalv1_desc);
985
986 if (sfp->features & OPAL_FEATURE_OPALV20x0040)
987 sedopal_print_opal_v2(sfp->opalv2_desc);
988
989 if (sfp->features & OPAL_FEATURE_TPER0x0001)
990 sedopal_print_tper(sfp->tper_desc);
991
992 if (sfp->features & OPAL_FEATURE_GEOMETRY0x0004)
993 sedopal_print_geometry(sfp->geometry_reporting_desc);
994
995 if (sfp->features & OPAL_FEATURE_OPALITE0x0080)
996 sedopal_print_opalite(sfp->opalite_desc);
997
998 if (sfp->features & OPAL_FEATURE_SINGLE_USER_MODE0x0010)
999 sedopal_print_sum(sfp->single_user_mode_desc);
1000
1001 if (sfp->features & OPAL_FEATURE_DATA_STORE0x0020)
1002 sedopal_print_datastore(sfp->datastore_desc);
1003
1004 if (sfp->features & OPAL_FEATURE_BLOCK_SID_AUTH0x1000)
1005 sedopal_print_sid_auth(sfp->block_sid_auth_desc);
1006
1007 if (sfp->features & OPAL_FEATURE_RUBY0x0400)
1008 sedopal_print_ruby(sfp->ruby_desc);
1009
1010 if (sfp->features & OPAL_FEATURE_PYRITE_V10x0100)
1011 sedopal_print_pyrite_v1(sfp->pyrite_v1_desc);
1012
1013 if (sfp->features & OPAL_FEATURE_PYRITE_V20x0200)
1014 sedopal_print_pyrite_v2(sfp->pyrite_v2_desc);
1015
1016 if (sfp->features & OPAL_FEATURE_LOCKING_LBA0x0800)
1017 sedopal_print_locking_lba(sfp->locking_lba_desc);
1018
1019 if (sfp->features & OPAL_FEATURE_CONFIG_NS_LOCKING0x2000)
1020 sedopal_print_config_ns(sfp->config_ns_desc);
1021
1022 if (sfp->features & OPAL_FEATURE_NS_GEOMETRY0x8000)
1023 sedopal_print_ns_geometry(sfp->ns_geometry_desc);
1024}
1025
1026/*
1027 * Query a drive to retrieve it's level 0 features.
1028 */
1029int sedopal_discover_device(int fd, struct level_0_discovery_features **feat,
1030 struct level_0_discovery_features **feat_end)
1031{
1032#ifdef IOC_OPAL_DISCOVERY(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((239)) << 0) | ((((sizeof(struct opal_discovery)))) <<
((0 +8)+8)))
1033 int rc;
1034 struct opal_discovery discover;
1035 struct level_0_discovery_header *dh;
1036
1037 discover.data = (uintptr_t)level0_discovery_buf;
1038 discover.size = sizeof(level0_discovery_buf);
1039
1040 rc = ioctl(fd, IOC_OPAL_DISCOVERY(((1U) << (((0 +8)+8)+14)) | ((('p')) << (0 +8)) |
(((239)) << 0) | ((((sizeof(struct opal_discovery)))) <<
((0 +8)+8)))
, &discover);
1041 if (rc < 0) {
1042 fprintf(stderrstderr, "Error: ioctl IOC_OPAL_DISCOVERY failed\n");
1043 return rc;
1044 }
1045
1046 /*
1047 * The returned buffer contains a level 0 discovery header
1048 * folowed by an array of level 0 feature records.
1049 *
1050 * TCG Opal Specification v2.0.2 section 3.1.1
1051 */
1052 dh = (struct level_0_discovery_header *)level0_discovery_buf;
1053 *feat = (struct level_0_discovery_features *)(dh + 1);
1054 *feat_end = (struct level_0_discovery_features *)
1055 (level0_discovery_buf + be32toh(dh->parameter_length)__bswap_32 (dh->parameter_length));
1056
1057 return 0
1058 ;
1059#else /* IOC_OPAL_DISCOVERY */
1060 fprintf(stderrstderr, "ERROR : NVMe device discovery is not supported\n");
1061 return -EOPNOTSUPP95;
1062#endif
1063}
1064
1065/*
1066 * Query a drive to determine if it's SED Opal capable and
1067 * it's current locking status.
1068 */
1069int sedopal_cmd_discover(int fd)
1070{
1071 int rc, feat_length;
1072 struct level_0_discovery_features *feat;
1073 struct level_0_discovery_features *feat_end;
1074 struct sedopal_feature_parser sfp = {};
1
'sfp.locking_desc' initialized to a null pointer value
1075
1076 rc = sedopal_discover_device(fd, &feat, &feat_end);
1077 if (rc
1.1
'rc' is equal to 0
!= 0)
2
Taking false branch
1078 return rc;
1079
1080 /*
1081 * iterate through all the features that were returned
1082 */
1083 while (feat < feat_end) {
3
Loop condition is true. Entering loop body
9
Assuming 'feat' is >= 'feat_end'
10
Loop condition is false. Execution continues on line 1090
1084 sedopal_parse_features(feat, &sfp);
4
Calling 'sedopal_parse_features'
8
Returning from 'sedopal_parse_features'
1085 feat_length = feat->length + 4 /* hdr */;
1086 feat = (struct level_0_discovery_features *)
1087 ((char *)feat + feat_length);
1088 }
1089
1090 rc = 0;
1091 if (!(sfp.features & OPAL_SED_LOCKING_SUPPORT(0x0008 | 0x0040 | 0x0400 | 0x0100 | 0x0200 | 0x0002))) {
11
Taking false branch
1092 fprintf(stderrstderr, "Error: device does not support SED Opal\n");
1093 rc = -1;
1094 } else
1095 sedopal_print_locking_features(sfp.locking_desc);
12
Passing null pointer value via 1st parameter 'data'
13
Calling 'sedopal_print_locking_features'
1096
1097 if (!sedopal_discovery_verbose)
1098 return rc;
1099
1100 sedopal_print_features(&sfp);
1101
1102
1103 return rc;
1104}
1105
1106/*
1107 * Query a drive to determine its locking state
1108 */
1109int sedopal_locking_state(int fd)
1110{
1111 int rc, feat_length;
1112 struct level_0_discovery_features *feat;
1113 struct level_0_discovery_features *feat_end;
1114
1115 rc = sedopal_discover_device(fd, &feat, &feat_end);
1116 if (rc != 0)
1117 return rc;
1118
1119 /*
1120 * iterate through all the features that were returned
1121 */
1122 while (feat < feat_end) {
1123 uint16_t code = be16toh(feat->code)__bswap_16 (feat->code);
1124
1125 if (code == OPAL_FEATURE_CODE_LOCKING0x0002) {
1126 struct locking_desc *ld = (struct locking_desc *) (feat + 1);
1127
1128 return ld->features;
1129 }
1130
1131 feat_length = feat->length + 4 /* hdr */;
1132 feat = (struct level_0_discovery_features *)
1133 ((char *)feat + feat_length);
1134 }
1135
1136 return 0;
1137}