uboot/lib/uuid.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2011 Calxeda, Inc.
   4 */
   5
   6#include <common.h>
   7#include <env.h>
   8#include <time.h>
   9#include <linux/ctype.h>
  10#include <errno.h>
  11#include <common.h>
  12#include <asm/io.h>
  13#include <part_efi.h>
  14#include <malloc.h>
  15
  16/*
  17 * UUID - Universally Unique IDentifier - 128 bits unique number.
  18 *        There are 5 versions and one variant of UUID defined by RFC4122
  19 *        specification. A UUID contains a set of fields. The set varies
  20 *        depending on the version of the UUID, as shown below:
  21 *        - time, MAC address(v1),
  22 *        - user ID(v2),
  23 *        - MD5 of name or URL(v3),
  24 *        - random data(v4),
  25 *        - SHA-1 of name or URL(v5),
  26 *
  27 * Layout of UUID:
  28 * timestamp - 60-bit: time_low, time_mid, time_hi_and_version
  29 * version   - 4 bit (bit 4 through 7 of the time_hi_and_version)
  30 * clock seq - 14 bit: clock_seq_hi_and_reserved, clock_seq_low
  31 * variant:  - bit 6 and 7 of clock_seq_hi_and_reserved
  32 * node      - 48 bit
  33 *
  34 * source: https://www.ietf.org/rfc/rfc4122.txt
  35 *
  36 * UUID binary format (16 bytes):
  37 *
  38 * 4B-2B-2B-2B-6B (big endian - network byte order)
  39 *
  40 * UUID string is 36 length of characters (36 bytes):
  41 *
  42 * 0        9    14   19   24
  43 * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  44 *    be     be   be   be       be
  45 *
  46 * where x is a hexadecimal character. Fields are separated by '-'s.
  47 * When converting to a binary UUID, le means the field should be converted
  48 * to little endian and be means it should be converted to big endian.
  49 *
  50 * UUID is also used as GUID (Globally Unique Identifier) with the same binary
  51 * format but it differs in string format like below.
  52 *
  53 * GUID:
  54 * 0        9    14   19   24
  55 * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  56 *    le     le   le   be       be
  57 *
  58 * GUID is used e.g. in GPT (GUID Partition Table) as a partiions unique id.
  59 */
  60int uuid_str_valid(const char *uuid)
  61{
  62        int i, valid;
  63
  64        if (uuid == NULL)
  65                return 0;
  66
  67        for (i = 0, valid = 1; uuid[i] && valid; i++) {
  68                switch (i) {
  69                case 8: case 13: case 18: case 23:
  70                        valid = (uuid[i] == '-');
  71                        break;
  72                default:
  73                        valid = isxdigit(uuid[i]);
  74                        break;
  75                }
  76        }
  77
  78        if (i != UUID_STR_LEN || !valid)
  79                return 0;
  80
  81        return 1;
  82}
  83
  84#ifdef CONFIG_PARTITION_TYPE_GUID
  85static const struct {
  86        const char *string;
  87        efi_guid_t guid;
  88} list_guid[] = {
  89        {"system",      PARTITION_SYSTEM_GUID},
  90        {"mbr",         LEGACY_MBR_PARTITION_GUID},
  91        {"msft",        PARTITION_MSFT_RESERVED_GUID},
  92        {"data",        PARTITION_BASIC_DATA_GUID},
  93        {"linux",       PARTITION_LINUX_FILE_SYSTEM_DATA_GUID},
  94        {"raid",        PARTITION_LINUX_RAID_GUID},
  95        {"swap",        PARTITION_LINUX_SWAP_GUID},
  96        {"lvm",         PARTITION_LINUX_LVM_GUID}
  97};
  98
  99/*
 100 * uuid_guid_get_bin() - this function get GUID bin for string
 101 *
 102 * @param guid_str - pointer to partition type string
 103 * @param guid_bin - pointer to allocated array for big endian output [16B]
 104 */
 105int uuid_guid_get_bin(const char *guid_str, unsigned char *guid_bin)
 106{
 107        int i;
 108
 109        for (i = 0; i < ARRAY_SIZE(list_guid); i++) {
 110                if (!strcmp(list_guid[i].string, guid_str)) {
 111                        memcpy(guid_bin, &list_guid[i].guid, 16);
 112                        return 0;
 113                }
 114        }
 115        return -ENODEV;
 116}
 117
 118/*
 119 * uuid_guid_get_str() - this function get string for GUID.
 120 *
 121 * @param guid_bin - pointer to string with partition type guid [16B]
 122 * @param guid_str - pointer to allocated partition type string [7B]
 123 */
 124int uuid_guid_get_str(unsigned char *guid_bin, char *guid_str)
 125{
 126        int i;
 127
 128        *guid_str = 0;
 129        for (i = 0; i < ARRAY_SIZE(list_guid); i++) {
 130                if (!memcmp(list_guid[i].guid.b, guid_bin, 16)) {
 131                        strcpy(guid_str, list_guid[i].string);
 132                        return 0;
 133                }
 134        }
 135        return -ENODEV;
 136}
 137#endif
 138
 139/*
 140 * uuid_str_to_bin() - convert string UUID or GUID to big endian binary data.
 141 *
 142 * @param uuid_str - pointer to UUID or GUID string [37B] or GUID shorcut
 143 * @param uuid_bin - pointer to allocated array for big endian output [16B]
 144 * @str_format     - UUID string format: 0 - UUID; 1 - GUID
 145 */
 146int uuid_str_to_bin(char *uuid_str, unsigned char *uuid_bin, int str_format)
 147{
 148        uint16_t tmp16;
 149        uint32_t tmp32;
 150        uint64_t tmp64;
 151
 152        if (!uuid_str_valid(uuid_str)) {
 153#ifdef CONFIG_PARTITION_TYPE_GUID
 154                if (!uuid_guid_get_bin(uuid_str, uuid_bin))
 155                        return 0;
 156#endif
 157                return -EINVAL;
 158        }
 159
 160        if (str_format == UUID_STR_FORMAT_STD) {
 161                tmp32 = cpu_to_be32(simple_strtoul(uuid_str, NULL, 16));
 162                memcpy(uuid_bin, &tmp32, 4);
 163
 164                tmp16 = cpu_to_be16(simple_strtoul(uuid_str + 9, NULL, 16));
 165                memcpy(uuid_bin + 4, &tmp16, 2);
 166
 167                tmp16 = cpu_to_be16(simple_strtoul(uuid_str + 14, NULL, 16));
 168                memcpy(uuid_bin + 6, &tmp16, 2);
 169        } else {
 170                tmp32 = cpu_to_le32(simple_strtoul(uuid_str, NULL, 16));
 171                memcpy(uuid_bin, &tmp32, 4);
 172
 173                tmp16 = cpu_to_le16(simple_strtoul(uuid_str + 9, NULL, 16));
 174                memcpy(uuid_bin + 4, &tmp16, 2);
 175
 176                tmp16 = cpu_to_le16(simple_strtoul(uuid_str + 14, NULL, 16));
 177                memcpy(uuid_bin + 6, &tmp16, 2);
 178        }
 179
 180        tmp16 = cpu_to_be16(simple_strtoul(uuid_str + 19, NULL, 16));
 181        memcpy(uuid_bin + 8, &tmp16, 2);
 182
 183        tmp64 = cpu_to_be64(simple_strtoull(uuid_str + 24, NULL, 16));
 184        memcpy(uuid_bin + 10, (char *)&tmp64 + 2, 6);
 185
 186        return 0;
 187}
 188
 189/*
 190 * uuid_bin_to_str() - convert big endian binary data to string UUID or GUID.
 191 *
 192 * @param uuid_bin:     pointer to binary data of UUID (big endian) [16B]
 193 * @param uuid_str:     pointer to allocated array for output string [37B]
 194 * @str_format:         bit 0: 0 - UUID; 1 - GUID
 195 *                      bit 1: 0 - lower case; 2 - upper case
 196 */
 197void uuid_bin_to_str(unsigned char *uuid_bin, char *uuid_str, int str_format)
 198{
 199        const u8 uuid_char_order[UUID_BIN_LEN] = {0, 1, 2, 3, 4, 5, 6, 7, 8,
 200                                                  9, 10, 11, 12, 13, 14, 15};
 201        const u8 guid_char_order[UUID_BIN_LEN] = {3, 2, 1, 0, 5, 4, 7, 6, 8,
 202                                                  9, 10, 11, 12, 13, 14, 15};
 203        const u8 *char_order;
 204        const char *format;
 205        int i;
 206
 207        /*
 208         * UUID and GUID bin data - always in big endian:
 209         * 4B-2B-2B-2B-6B
 210         * be be be be be
 211         */
 212        if (str_format & UUID_STR_FORMAT_GUID)
 213                char_order = guid_char_order;
 214        else
 215                char_order = uuid_char_order;
 216        if (str_format & UUID_STR_UPPER_CASE)
 217                format = "%02X";
 218        else
 219                format = "%02x";
 220
 221        for (i = 0; i < 16; i++) {
 222                sprintf(uuid_str, format, uuid_bin[char_order[i]]);
 223                uuid_str += 2;
 224                switch (i) {
 225                case 3:
 226                case 5:
 227                case 7:
 228                case 9:
 229                        *uuid_str++ = '-';
 230                        break;
 231                }
 232        }
 233}
 234
 235/*
 236 * gen_rand_uuid() - this function generates a random binary UUID version 4.
 237 *                   In this version all fields beside 4 bits of version and
 238 *                   2 bits of variant are randomly generated.
 239 *
 240 * @param uuid_bin - pointer to allocated array [16B]. Output is in big endian.
 241*/
 242#if defined(CONFIG_RANDOM_UUID) || defined(CONFIG_CMD_UUID)
 243void gen_rand_uuid(unsigned char *uuid_bin)
 244{
 245        u32 ptr[4];
 246        struct uuid *uuid = (struct uuid *)ptr;
 247        int i;
 248
 249        srand(get_ticks() + rand());
 250
 251        /* Set all fields randomly */
 252        for (i = 0; i < 4; i++)
 253                ptr[i] = rand();
 254
 255        clrsetbits_be16(&uuid->time_hi_and_version,
 256                        UUID_VERSION_MASK,
 257                        UUID_VERSION << UUID_VERSION_SHIFT);
 258
 259        clrsetbits_8(&uuid->clock_seq_hi_and_reserved,
 260                     UUID_VARIANT_MASK,
 261                     UUID_VARIANT << UUID_VARIANT_SHIFT);
 262
 263        memcpy(uuid_bin, uuid, 16);
 264}
 265
 266/*
 267 * gen_rand_uuid_str() - this function generates UUID v4 (random) in two string
 268 *                       formats UUID or GUID.
 269 *
 270 * @param uuid_str - pointer to allocated array [37B].
 271 * @param          - uuid output type: UUID - 0, GUID - 1
 272 */
 273void gen_rand_uuid_str(char *uuid_str, int str_format)
 274{
 275        unsigned char uuid_bin[UUID_BIN_LEN];
 276
 277        /* Generate UUID (big endian) */
 278        gen_rand_uuid(uuid_bin);
 279
 280        /* Convert UUID bin to UUID or GUID formated STRING  */
 281        uuid_bin_to_str(uuid_bin, uuid_str, str_format);
 282}
 283
 284#if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_CMD_UUID)
 285int do_uuid(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 286{
 287        char uuid[UUID_STR_LEN + 1];
 288        int str_format;
 289
 290        if (!strcmp(argv[0], "uuid"))
 291                str_format = UUID_STR_FORMAT_STD;
 292        else
 293                str_format = UUID_STR_FORMAT_GUID;
 294
 295        if (argc > 2)
 296                return CMD_RET_USAGE;
 297
 298        gen_rand_uuid_str(uuid, str_format);
 299
 300        if (argc == 1)
 301                printf("%s\n", uuid);
 302        else
 303                env_set(argv[1], uuid);
 304
 305        return CMD_RET_SUCCESS;
 306}
 307
 308U_BOOT_CMD(uuid, CONFIG_SYS_MAXARGS, 1, do_uuid,
 309           "UUID - generate random Universally Unique Identifier",
 310           "[<varname>]\n"
 311           "Argument:\n"
 312           "varname: for set result in a environment variable\n"
 313           "e.g. uuid uuid_env"
 314);
 315
 316U_BOOT_CMD(guid, CONFIG_SYS_MAXARGS, 1, do_uuid,
 317           "GUID - generate Globally Unique Identifier based on random UUID",
 318           "[<varname>]\n"
 319           "Argument:\n"
 320           "varname: for set result in a environment variable\n"
 321           "e.g. guid guid_env"
 322);
 323#endif /* CONFIG_CMD_UUID */
 324#endif /* CONFIG_RANDOM_UUID || CONFIG_CMD_UUID */
 325