.travis.yml: Shorten the runtime of the problematic jobs
[qemu/kevin.git] / hw / nvme / nguid.c
blob829832bd9f41a8f7bb0e6d635ce2cd184be93cf0
1 /*
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
14 * for more details.
17 #include "qemu/osdep.h"
18 #include "qapi/visitor.h"
19 #include "qemu/ctype.h"
20 #include "nvme.h"
22 #define NGUID_SEPARATOR '-'
24 #define NGUID_VALUE_AUTO "auto"
26 #define NGUID_FMT \
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)
42 int i;
43 uint32_t x;
45 QEMU_BUILD_BUG_ON((NGUID_LEN % sizeof(x)) != 0);
47 for (i = 0; i < NGUID_LEN; i += sizeof(x)) {
48 x = g_random_int();
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)
81 int i;
82 int digit_count = 0;
84 for (i = 0; i < strlen(str); i++) {
85 const char c = str[i];
86 if (qemu_isxdigit(c)) {
87 digit_count++;
88 continue;
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
94 * back "--".
96 if ((i > 0) && (str[i - 1] != NGUID_SEPARATOR) &&
97 (digit_count % 2) == 0) {
98 continue;
101 return false;
104 * The string should have the correct byte length and not finish with the
105 * separator
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];
113 int ret = 0;
114 int i;
115 const char *ptr = str;
117 if (!nvme_nguid_is_valid(str)) {
118 return -1;
121 for (i = 0; i < NGUID_LEN; i++) {
122 ret = sscanf(ptr, "%02hhx", &id[i]);
123 if (ret != 1) {
124 return -1;
126 ptr += 2;
127 if (*ptr == NGUID_SEPARATOR) {
128 ptr++;
132 return 0;
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,
150 Error **errp)
152 Property *prop = opaque;
153 NvmeNGUID *nguid = object_field_prop_ptr(obj, prop);
154 char buffer[NGUID_STR_LEN];
155 char *p = buffer;
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,
163 Error **errp)
165 Property *prop = opaque;
166 NvmeNGUID *nguid = object_field_prop_ptr(obj, prop);
167 char *str;
169 if (!visit_type_str(v, name, &str, errp)) {
170 return;
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);
178 g_free(str);
181 const PropertyInfo qdev_prop_nguid = {
182 .name = "str",
183 .description =
184 "NGUID or \"" NGUID_VALUE_AUTO "\" for random value",
185 .get = get_nguid,
186 .set = set_nguid,