2 * QEMU NVMe NGUID functions
4 * Copyright 2024 Google LLC
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 #include "qemu/osdep.h"
18 #include "qapi/visitor.h"
19 #include "qemu/ctype.h"
22 #define NGUID_SEPARATOR '-'
24 #define NGUID_VALUE_AUTO "auto"
27 "%02hhx%02hhx%02hhx%02hhx" \
28 "%02hhx%02hhx%02hhx%02hhx" \
29 "%02hhx%02hhx%02hhx%02hhx" \
30 "%02hhx%02hhx%02hhx%02hhx"
32 #define NGUID_STR_LEN (2 * NGUID_LEN + 1)
34 bool nvme_nguid_is_null(const NvmeNGUID
*nguid
)
36 static NvmeNGUID null_nguid
;
37 return memcmp(nguid
, &null_nguid
, sizeof(NvmeNGUID
)) == 0;
40 static void nvme_nguid_generate(NvmeNGUID
*out
)
45 QEMU_BUILD_BUG_ON((NGUID_LEN
% sizeof(x
)) != 0);
47 for (i
= 0; i
< NGUID_LEN
; i
+= sizeof(x
)) {
49 memcpy(&out
->data
[i
], &x
, sizeof(x
));
54 * The Linux Kernel typically prints the NGUID of an NVMe namespace using the
55 * same format as the UUID. For instance:
57 * $ cat /sys/class/block/nvme0n1/nguid
58 * e9accd3b-8390-4e13-167c-f0593437f57d
60 * When there is no UUID but there is NGUID the Kernel will print the NGUID as
61 * wwid and it won't use the UUID format:
63 * $ cat /sys/class/block/nvme0n1/wwid
64 * eui.e9accd3b83904e13167cf0593437f57d
66 * The NGUID has different fields compared to the UUID, so the grouping used in
67 * the UUID format has no relation with the 3 fields of the NGUID.
69 * This implementation won't expect a strict format as the UUID one and instead
70 * it will admit any string of hexadecimal digits. Byte groups could be created
71 * using the '-' separator. The number of bytes needs to be exactly 16 and the
72 * separator '-' has to be exactly in a byte boundary. The following are
73 * examples of accepted formats for the NGUID string:
75 * nguid="e9accd3b-8390-4e13-167c-f0593437f57d"
76 * nguid="e9accd3b83904e13167cf0593437f57d"
77 * nguid="FEDCBA9876543210-ABCDEF-0123456789"
79 static bool nvme_nguid_is_valid(const char *str
)
84 for (i
= 0; i
< strlen(str
); i
++) {
85 const char c
= str
[i
];
86 if (qemu_isxdigit(c
)) {
90 if (c
== NGUID_SEPARATOR
) {
92 * We need to make sure the separator is in a byte boundary, the
93 * string does not start with the separator and they are not back to
96 if ((i
> 0) && (str
[i
- 1] != NGUID_SEPARATOR
) &&
97 (digit_count
% 2) == 0) {
104 * The string should have the correct byte length and not finish with the
107 return (digit_count
== (2 * NGUID_LEN
)) && (str
[i
- 1] != NGUID_SEPARATOR
);
110 static int nvme_nguid_parse(const char *str
, NvmeNGUID
*nguid
)
112 uint8_t *id
= &nguid
->data
[0];
115 const char *ptr
= str
;
117 if (!nvme_nguid_is_valid(str
)) {
121 for (i
= 0; i
< NGUID_LEN
; i
++) {
122 ret
= sscanf(ptr
, "%02hhx", &id
[i
]);
127 if (*ptr
== NGUID_SEPARATOR
) {
136 * When converted back to string this implementation will use a raw hex number
137 * with no separators, for instance:
139 * "e9accd3b83904e13167cf0593437f57d"
141 static void nvme_nguid_stringify(const NvmeNGUID
*nguid
, char *out
)
143 const uint8_t *id
= &nguid
->data
[0];
144 snprintf(out
, NGUID_STR_LEN
, NGUID_FMT
,
145 id
[0], id
[1], id
[2], id
[3], id
[4], id
[5], id
[6], id
[7],
146 id
[8], id
[9], id
[10], id
[11], id
[12], id
[13], id
[14], id
[15]);
149 static void get_nguid(Object
*obj
, Visitor
*v
, const char *name
, void *opaque
,
152 Property
*prop
= opaque
;
153 NvmeNGUID
*nguid
= object_field_prop_ptr(obj
, prop
);
154 char buffer
[NGUID_STR_LEN
];
157 nvme_nguid_stringify(nguid
, buffer
);
159 visit_type_str(v
, name
, &p
, errp
);
162 static void set_nguid(Object
*obj
, Visitor
*v
, const char *name
, void *opaque
,
165 Property
*prop
= opaque
;
166 NvmeNGUID
*nguid
= object_field_prop_ptr(obj
, prop
);
169 if (!visit_type_str(v
, name
, &str
, errp
)) {
173 if (!strcmp(str
, NGUID_VALUE_AUTO
)) {
174 nvme_nguid_generate(nguid
);
175 } else if (nvme_nguid_parse(str
, nguid
) < 0) {
176 error_set_from_qdev_prop_error(errp
, EINVAL
, obj
, name
, str
);
181 const PropertyInfo qdev_prop_nguid
= {
184 "NGUID or \"" NGUID_VALUE_AUTO
"\" for random value",