1*bdc31646SRoque Arcudia Hernandez /* 2*bdc31646SRoque Arcudia Hernandez * QEMU NVMe NGUID functions 3*bdc31646SRoque Arcudia Hernandez * 4*bdc31646SRoque Arcudia Hernandez * Copyright 2024 Google LLC 5*bdc31646SRoque Arcudia Hernandez * 6*bdc31646SRoque Arcudia Hernandez * This program is free software; you can redistribute it and/or modify it 7*bdc31646SRoque Arcudia Hernandez * under the terms of the GNU General Public License as published by the 8*bdc31646SRoque Arcudia Hernandez * Free Software Foundation; either version 2 of the License, or 9*bdc31646SRoque Arcudia Hernandez * (at your option) any later version. 10*bdc31646SRoque Arcudia Hernandez * 11*bdc31646SRoque Arcudia Hernandez * This program is distributed in the hope that it will be useful, but WITHOUT 12*bdc31646SRoque Arcudia Hernandez * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13*bdc31646SRoque Arcudia Hernandez * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14*bdc31646SRoque Arcudia Hernandez * for more details. 15*bdc31646SRoque Arcudia Hernandez */ 16*bdc31646SRoque Arcudia Hernandez 17*bdc31646SRoque Arcudia Hernandez #include "qemu/osdep.h" 18*bdc31646SRoque Arcudia Hernandez #include "qapi/visitor.h" 19*bdc31646SRoque Arcudia Hernandez #include "qemu/ctype.h" 20*bdc31646SRoque Arcudia Hernandez #include "nvme.h" 21*bdc31646SRoque Arcudia Hernandez 22*bdc31646SRoque Arcudia Hernandez #define NGUID_SEPARATOR '-' 23*bdc31646SRoque Arcudia Hernandez 24*bdc31646SRoque Arcudia Hernandez #define NGUID_VALUE_AUTO "auto" 25*bdc31646SRoque Arcudia Hernandez 26*bdc31646SRoque Arcudia Hernandez #define NGUID_FMT \ 27*bdc31646SRoque Arcudia Hernandez "%02hhx%02hhx%02hhx%02hhx" \ 28*bdc31646SRoque Arcudia Hernandez "%02hhx%02hhx%02hhx%02hhx" \ 29*bdc31646SRoque Arcudia Hernandez "%02hhx%02hhx%02hhx%02hhx" \ 30*bdc31646SRoque Arcudia Hernandez "%02hhx%02hhx%02hhx%02hhx" 31*bdc31646SRoque Arcudia Hernandez 32*bdc31646SRoque Arcudia Hernandez #define NGUID_STR_LEN (2 * NGUID_LEN + 1) 33*bdc31646SRoque Arcudia Hernandez 34*bdc31646SRoque Arcudia Hernandez bool nvme_nguid_is_null(const NvmeNGUID *nguid) 35*bdc31646SRoque Arcudia Hernandez { 36*bdc31646SRoque Arcudia Hernandez static NvmeNGUID null_nguid; 37*bdc31646SRoque Arcudia Hernandez return memcmp(nguid, &null_nguid, sizeof(NvmeNGUID)) == 0; 38*bdc31646SRoque Arcudia Hernandez } 39*bdc31646SRoque Arcudia Hernandez 40*bdc31646SRoque Arcudia Hernandez static void nvme_nguid_generate(NvmeNGUID *out) 41*bdc31646SRoque Arcudia Hernandez { 42*bdc31646SRoque Arcudia Hernandez int i; 43*bdc31646SRoque Arcudia Hernandez uint32_t x; 44*bdc31646SRoque Arcudia Hernandez 45*bdc31646SRoque Arcudia Hernandez QEMU_BUILD_BUG_ON((NGUID_LEN % sizeof(x)) != 0); 46*bdc31646SRoque Arcudia Hernandez 47*bdc31646SRoque Arcudia Hernandez for (i = 0; i < NGUID_LEN; i += sizeof(x)) { 48*bdc31646SRoque Arcudia Hernandez x = g_random_int(); 49*bdc31646SRoque Arcudia Hernandez memcpy(&out->data[i], &x, sizeof(x)); 50*bdc31646SRoque Arcudia Hernandez } 51*bdc31646SRoque Arcudia Hernandez } 52*bdc31646SRoque Arcudia Hernandez 53*bdc31646SRoque Arcudia Hernandez /* 54*bdc31646SRoque Arcudia Hernandez * The Linux Kernel typically prints the NGUID of an NVMe namespace using the 55*bdc31646SRoque Arcudia Hernandez * same format as the UUID. For instance: 56*bdc31646SRoque Arcudia Hernandez * 57*bdc31646SRoque Arcudia Hernandez * $ cat /sys/class/block/nvme0n1/nguid 58*bdc31646SRoque Arcudia Hernandez * e9accd3b-8390-4e13-167c-f0593437f57d 59*bdc31646SRoque Arcudia Hernandez * 60*bdc31646SRoque Arcudia Hernandez * When there is no UUID but there is NGUID the Kernel will print the NGUID as 61*bdc31646SRoque Arcudia Hernandez * wwid and it won't use the UUID format: 62*bdc31646SRoque Arcudia Hernandez * 63*bdc31646SRoque Arcudia Hernandez * $ cat /sys/class/block/nvme0n1/wwid 64*bdc31646SRoque Arcudia Hernandez * eui.e9accd3b83904e13167cf0593437f57d 65*bdc31646SRoque Arcudia Hernandez * 66*bdc31646SRoque Arcudia Hernandez * The NGUID has different fields compared to the UUID, so the grouping used in 67*bdc31646SRoque Arcudia Hernandez * the UUID format has no relation with the 3 fields of the NGUID. 68*bdc31646SRoque Arcudia Hernandez * 69*bdc31646SRoque Arcudia Hernandez * This implementation won't expect a strict format as the UUID one and instead 70*bdc31646SRoque Arcudia Hernandez * it will admit any string of hexadecimal digits. Byte groups could be created 71*bdc31646SRoque Arcudia Hernandez * using the '-' separator. The number of bytes needs to be exactly 16 and the 72*bdc31646SRoque Arcudia Hernandez * separator '-' has to be exactly in a byte boundary. The following are 73*bdc31646SRoque Arcudia Hernandez * examples of accepted formats for the NGUID string: 74*bdc31646SRoque Arcudia Hernandez * 75*bdc31646SRoque Arcudia Hernandez * nguid="e9accd3b-8390-4e13-167c-f0593437f57d" 76*bdc31646SRoque Arcudia Hernandez * nguid="e9accd3b83904e13167cf0593437f57d" 77*bdc31646SRoque Arcudia Hernandez * nguid="FEDCBA9876543210-ABCDEF-0123456789" 78*bdc31646SRoque Arcudia Hernandez */ 79*bdc31646SRoque Arcudia Hernandez static bool nvme_nguid_is_valid(const char *str) 80*bdc31646SRoque Arcudia Hernandez { 81*bdc31646SRoque Arcudia Hernandez int i; 82*bdc31646SRoque Arcudia Hernandez int digit_count = 0; 83*bdc31646SRoque Arcudia Hernandez 84*bdc31646SRoque Arcudia Hernandez for (i = 0; i < strlen(str); i++) { 85*bdc31646SRoque Arcudia Hernandez const char c = str[i]; 86*bdc31646SRoque Arcudia Hernandez if (qemu_isxdigit(c)) { 87*bdc31646SRoque Arcudia Hernandez digit_count++; 88*bdc31646SRoque Arcudia Hernandez continue; 89*bdc31646SRoque Arcudia Hernandez } 90*bdc31646SRoque Arcudia Hernandez if (c == NGUID_SEPARATOR) { 91*bdc31646SRoque Arcudia Hernandez /* 92*bdc31646SRoque Arcudia Hernandez * We need to make sure the separator is in a byte boundary, the 93*bdc31646SRoque Arcudia Hernandez * string does not start with the separator and they are not back to 94*bdc31646SRoque Arcudia Hernandez * back "--". 95*bdc31646SRoque Arcudia Hernandez */ 96*bdc31646SRoque Arcudia Hernandez if ((i > 0) && (str[i - 1] != NGUID_SEPARATOR) && 97*bdc31646SRoque Arcudia Hernandez (digit_count % 2) == 0) { 98*bdc31646SRoque Arcudia Hernandez continue; 99*bdc31646SRoque Arcudia Hernandez } 100*bdc31646SRoque Arcudia Hernandez } 101*bdc31646SRoque Arcudia Hernandez return false; 102*bdc31646SRoque Arcudia Hernandez } 103*bdc31646SRoque Arcudia Hernandez /* 104*bdc31646SRoque Arcudia Hernandez * The string should have the correct byte length and not finish with the 105*bdc31646SRoque Arcudia Hernandez * separator 106*bdc31646SRoque Arcudia Hernandez */ 107*bdc31646SRoque Arcudia Hernandez return (digit_count == (2 * NGUID_LEN)) && (str[i - 1] != NGUID_SEPARATOR); 108*bdc31646SRoque Arcudia Hernandez } 109*bdc31646SRoque Arcudia Hernandez 110*bdc31646SRoque Arcudia Hernandez static int nvme_nguid_parse(const char *str, NvmeNGUID *nguid) 111*bdc31646SRoque Arcudia Hernandez { 112*bdc31646SRoque Arcudia Hernandez uint8_t *id = &nguid->data[0]; 113*bdc31646SRoque Arcudia Hernandez int ret = 0; 114*bdc31646SRoque Arcudia Hernandez int i; 115*bdc31646SRoque Arcudia Hernandez const char *ptr = str; 116*bdc31646SRoque Arcudia Hernandez 117*bdc31646SRoque Arcudia Hernandez if (!nvme_nguid_is_valid(str)) { 118*bdc31646SRoque Arcudia Hernandez return -1; 119*bdc31646SRoque Arcudia Hernandez } 120*bdc31646SRoque Arcudia Hernandez 121*bdc31646SRoque Arcudia Hernandez for (i = 0; i < NGUID_LEN; i++) { 122*bdc31646SRoque Arcudia Hernandez ret = sscanf(ptr, "%02hhx", &id[i]); 123*bdc31646SRoque Arcudia Hernandez if (ret != 1) { 124*bdc31646SRoque Arcudia Hernandez return -1; 125*bdc31646SRoque Arcudia Hernandez } 126*bdc31646SRoque Arcudia Hernandez ptr += 2; 127*bdc31646SRoque Arcudia Hernandez if (*ptr == NGUID_SEPARATOR) { 128*bdc31646SRoque Arcudia Hernandez ptr++; 129*bdc31646SRoque Arcudia Hernandez } 130*bdc31646SRoque Arcudia Hernandez } 131*bdc31646SRoque Arcudia Hernandez 132*bdc31646SRoque Arcudia Hernandez return 0; 133*bdc31646SRoque Arcudia Hernandez } 134*bdc31646SRoque Arcudia Hernandez 135*bdc31646SRoque Arcudia Hernandez /* 136*bdc31646SRoque Arcudia Hernandez * When converted back to string this implementation will use a raw hex number 137*bdc31646SRoque Arcudia Hernandez * with no separators, for instance: 138*bdc31646SRoque Arcudia Hernandez * 139*bdc31646SRoque Arcudia Hernandez * "e9accd3b83904e13167cf0593437f57d" 140*bdc31646SRoque Arcudia Hernandez */ 141*bdc31646SRoque Arcudia Hernandez static void nvme_nguid_stringify(const NvmeNGUID *nguid, char *out) 142*bdc31646SRoque Arcudia Hernandez { 143*bdc31646SRoque Arcudia Hernandez const uint8_t *id = &nguid->data[0]; 144*bdc31646SRoque Arcudia Hernandez snprintf(out, NGUID_STR_LEN, NGUID_FMT, 145*bdc31646SRoque Arcudia Hernandez id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], 146*bdc31646SRoque Arcudia Hernandez id[8], id[9], id[10], id[11], id[12], id[13], id[14], id[15]); 147*bdc31646SRoque Arcudia Hernandez } 148*bdc31646SRoque Arcudia Hernandez 149*bdc31646SRoque Arcudia Hernandez static void get_nguid(Object *obj, Visitor *v, const char *name, void *opaque, 150*bdc31646SRoque Arcudia Hernandez Error **errp) 151*bdc31646SRoque Arcudia Hernandez { 152*bdc31646SRoque Arcudia Hernandez Property *prop = opaque; 153*bdc31646SRoque Arcudia Hernandez NvmeNGUID *nguid = object_field_prop_ptr(obj, prop); 154*bdc31646SRoque Arcudia Hernandez char buffer[NGUID_STR_LEN]; 155*bdc31646SRoque Arcudia Hernandez char *p = buffer; 156*bdc31646SRoque Arcudia Hernandez 157*bdc31646SRoque Arcudia Hernandez nvme_nguid_stringify(nguid, buffer); 158*bdc31646SRoque Arcudia Hernandez 159*bdc31646SRoque Arcudia Hernandez visit_type_str(v, name, &p, errp); 160*bdc31646SRoque Arcudia Hernandez } 161*bdc31646SRoque Arcudia Hernandez 162*bdc31646SRoque Arcudia Hernandez static void set_nguid(Object *obj, Visitor *v, const char *name, void *opaque, 163*bdc31646SRoque Arcudia Hernandez Error **errp) 164*bdc31646SRoque Arcudia Hernandez { 165*bdc31646SRoque Arcudia Hernandez Property *prop = opaque; 166*bdc31646SRoque Arcudia Hernandez NvmeNGUID *nguid = object_field_prop_ptr(obj, prop); 167*bdc31646SRoque Arcudia Hernandez char *str; 168*bdc31646SRoque Arcudia Hernandez 169*bdc31646SRoque Arcudia Hernandez if (!visit_type_str(v, name, &str, errp)) { 170*bdc31646SRoque Arcudia Hernandez return; 171*bdc31646SRoque Arcudia Hernandez } 172*bdc31646SRoque Arcudia Hernandez 173*bdc31646SRoque Arcudia Hernandez if (!strcmp(str, NGUID_VALUE_AUTO)) { 174*bdc31646SRoque Arcudia Hernandez nvme_nguid_generate(nguid); 175*bdc31646SRoque Arcudia Hernandez } else if (nvme_nguid_parse(str, nguid) < 0) { 176*bdc31646SRoque Arcudia Hernandez error_set_from_qdev_prop_error(errp, EINVAL, obj, name, str); 177*bdc31646SRoque Arcudia Hernandez } 178*bdc31646SRoque Arcudia Hernandez g_free(str); 179*bdc31646SRoque Arcudia Hernandez } 180*bdc31646SRoque Arcudia Hernandez 181*bdc31646SRoque Arcudia Hernandez const PropertyInfo qdev_prop_nguid = { 182*bdc31646SRoque Arcudia Hernandez .name = "str", 183*bdc31646SRoque Arcudia Hernandez .description = 184*bdc31646SRoque Arcudia Hernandez "NGUID or \"" NGUID_VALUE_AUTO "\" for random value", 185*bdc31646SRoque Arcudia Hernandez .get = get_nguid, 186*bdc31646SRoque Arcudia Hernandez .set = set_nguid, 187*bdc31646SRoque Arcudia Hernandez }; 188