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