diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 11:06:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 11:06:51 +0000 |
commit | 86fcc93b6dde545a549437629292af70f0af0e9d (patch) | |
tree | 10be690f14e138dd6a5e07326e354ced15f52835 /src | |
parent | Releasing progress-linux version 1.8-3~progress7.99u1. (diff) | |
download | libnvme-86fcc93b6dde545a549437629292af70f0af0e9d.tar.xz libnvme-86fcc93b6dde545a549437629292af70f0af0e9d.zip |
Merging upstream version 1.9.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/libnvme.map | 13 | ||||
-rw-r--r-- | src/meson.build | 4 | ||||
-rw-r--r-- | src/nvme/crc32.c | 111 | ||||
-rw-r--r-- | src/nvme/crc32.h | 9 | ||||
-rw-r--r-- | src/nvme/fabrics.c | 39 | ||||
-rw-r--r-- | src/nvme/fabrics.h | 4 | ||||
-rw-r--r-- | src/nvme/filters.c | 62 | ||||
-rw-r--r-- | src/nvme/ioctl.c | 185 | ||||
-rw-r--r-- | src/nvme/ioctl.h | 156 | ||||
-rw-r--r-- | src/nvme/json.c | 95 | ||||
-rw-r--r-- | src/nvme/linux.c | 275 | ||||
-rw-r--r-- | src/nvme/linux.h | 130 | ||||
-rw-r--r-- | src/nvme/log.c | 25 | ||||
-rw-r--r-- | src/nvme/log.h | 31 | ||||
-rw-r--r-- | src/nvme/nbft.c | 6 | ||||
-rw-r--r-- | src/nvme/nbft.h | 110 | ||||
-rw-r--r-- | src/nvme/private.h | 15 | ||||
-rw-r--r-- | src/nvme/sysfs.c | 86 | ||||
-rw-r--r-- | src/nvme/tree.c | 71 | ||||
-rw-r--r-- | src/nvme/tree.h | 8 | ||||
-rw-r--r-- | src/nvme/types.h | 623 | ||||
-rw-r--r-- | src/nvme/util.c | 16 | ||||
-rw-r--r-- | src/nvme/util.h | 4 |
23 files changed, 1645 insertions, 433 deletions
diff --git a/src/libnvme.map b/src/libnvme.map index c8163cb..8710c41 100644 --- a/src/libnvme.map +++ b/src/libnvme.map @@ -1,4 +1,17 @@ # SPDX-License-Identifier: LGPL-2.1-or-later +LIBNVME_1.9 { + global: + nvme_export_tls_key; + nvme_get_logging_level; + nvme_import_tls_key; + nvme_read_key; + nvme_scan_tls_keys; + nvme_submit_passthru; + nvme_submit_passthru64; + nvme_update_key; + nvme_ctrl_get_cntlid; +}; + LIBNVME_1_8 { global: nvme_uuid_find; diff --git a/src/meson.build b/src/meson.build index 811f0f8..001c3b9 100644 --- a/src/meson.build +++ b/src/meson.build @@ -12,9 +12,11 @@ sources = [ 'nvme/ioctl.c', 'nvme/linux.c', 'nvme/log.c', + 'nvme/sysfs.c', 'nvme/tree.c', 'nvme/util.c', - 'nvme/base64.c' + 'nvme/base64.c', + 'nvme/crc32.c' ] mi_sources = [ diff --git a/src/nvme/crc32.c b/src/nvme/crc32.c new file mode 100644 index 0000000..e726c9f --- /dev/null +++ b/src/nvme/crc32.c @@ -0,0 +1,111 @@ +/* + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + */ + +/* + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1 + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to hight-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera + * tions for all combinations of data and CRC register values + * + * The values must be right-shifted by eight bits by the "updcrc + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions + * polynomial $edb88320 + * + * + * CRC32 code derived from work by Gary S. Brown. + */ + +/* https://web.mit.edu/freebsd/head/sys/libkern/crc32.c */ + +#include "crc32.h" + +const uint32_t crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +/* + * A function that calculates the CRC-32 based on the table above is + * given below for documentation purposes. An equivalent implementation + * of this function that's actually used in the kernel can be found + * in sys/libkern.h, where it can be inlined. + */ + +uint32_t +crc32(uint32_t crc, const void *buf, size_t size) +{ + const uint8_t *p = buf; + + crc = ~crc; + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + return ~crc; +} diff --git a/src/nvme/crc32.h b/src/nvme/crc32.h new file mode 100644 index 0000000..2bdc8fb --- /dev/null +++ b/src/nvme/crc32.h @@ -0,0 +1,9 @@ +#ifndef crc32_H +#define crc32_H + +#include <stdint.h> +#include <stddef.h> + +uint32_t crc32(uint32_t crc, const void *buf, size_t len); + +#endif diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c index 1f50229..6738e9d 100644 --- a/src/nvme/fabrics.c +++ b/src/nvme/fabrics.c @@ -1186,29 +1186,12 @@ struct nvmf_discovery_log *nvmf_get_discovery_wargs(struct nvme_get_discovery_ar return log; } -#define PATH_UUID_IBM "/proc/device-tree/ibm,partition-uuid" - -static char *uuid_ibm_filename(void) -{ - char *basepath = getenv("LIBNVME_SYSFS_PATH"); - char *str; - - if (!basepath) - return strdup(PATH_UUID_IBM); - - if (!asprintf(&str, "%s" PATH_UUID_IBM, basepath)) - return NULL; - - return str; -} - static int uuid_from_device_tree(char *system_uuid) { - _cleanup_free_ char *filename = uuid_ibm_filename(); _cleanup_fd_ int f = -1; ssize_t len; - f = open(filename, O_RDONLY); + f = open(nvme_uuid_ibm_filename(), O_RDONLY); if (f < 0) return -ENXIO; @@ -1220,22 +1203,6 @@ static int uuid_from_device_tree(char *system_uuid) return strlen(system_uuid) ? 0 : -ENXIO; } -#define PATH_DMI_ENTRIES "/sys/firmware/dmi/entries" - -static char *dmi_entries_dir(void) -{ - char *basepath = getenv("LIBNVME_SYSFS_PATH"); - char *str; - - if (!basepath) - return strdup(PATH_DMI_ENTRIES); - - if (!asprintf(&str, "%s" PATH_DMI_ENTRIES, basepath)) - return NULL; - - return str; -} - /* * See System Management BIOS (SMBIOS) Reference Specification * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf @@ -1264,7 +1231,7 @@ static bool is_dmi_uuid_valid(const char *buf, size_t len) static int uuid_from_dmi_entries(char *system_uuid) { _cleanup_dir_ DIR *d = NULL; - _cleanup_free_ char *entries_dir = dmi_entries_dir(); + const char *entries_dir = nvme_dmi_entries_dir(); int f; struct dirent *de; char buf[512] = {0}; @@ -1297,6 +1264,8 @@ static int uuid_from_dmi_entries(char *system_uuid) continue; len = read(f, buf, 512); close(f); + if (len <= 0) + continue; if (!is_dmi_uuid_valid(buf, len)) continue; diff --git a/src/nvme/fabrics.h b/src/nvme/fabrics.h index a2504de..4ebeb35 100644 --- a/src/nvme/fabrics.h +++ b/src/nvme/fabrics.h @@ -56,8 +56,8 @@ struct nvme_fabrics_config { int nr_write_queues; int nr_poll_queues; int tos; - int keyring; - int tls_key; + long keyring; + long tls_key; bool duplicate_connect; bool disable_sqflow; diff --git a/src/nvme/filters.c b/src/nvme/filters.c index 312b8f6..ceaba68 100644 --- a/src/nvme/filters.c +++ b/src/nvme/filters.c @@ -6,68 +6,12 @@ * Authors: Keith Busch <keith.busch@wdc.com> * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com> */ -#include <stdlib.h> #include <stdio.h> #include <string.h> #include <dirent.h> -#include <libgen.h> - -#include <sys/param.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <fcntl.h> -#include <unistd.h> #include "filters.h" -#include "types.h" -#include "util.h" -#include "cleanup.h" - -#define PATH_SYSFS_NVME "/sys/class/nvme" -#define PATH_SYSFS_NVME_SUBSYSTEM "/sys/class/nvme-subsystem" -#define PATH_SYSFS_BLOCK "/sys/block" - -char *nvme_ctrl_sysfs_dir(void) -{ - char *basepath = getenv("LIBNVME_SYSFS_PATH"); - char *str; - - if (!basepath) - return strdup(PATH_SYSFS_NVME); - - if (!asprintf(&str, "%s" PATH_SYSFS_NVME, basepath)) - return NULL; - - return str; -} - -char *nvme_ns_sysfs_dir(void) -{ - char *basepath = getenv("LIBNVME_SYSFS_PATH"); - char *str; - - if (!basepath) - return strdup(PATH_SYSFS_BLOCK); - - if (!asprintf(&str, "%s" PATH_SYSFS_BLOCK, basepath)) - return NULL; - - return str; -} - -char *nvme_subsys_sysfs_dir(void) -{ - char *basepath = getenv("LIBNVME_SYSFS_PATH"); - char *str; - - if (!basepath) - return strdup(PATH_SYSFS_NVME_SUBSYSTEM); - - if (!asprintf(&str, "%s" PATH_SYSFS_NVME_SUBSYSTEM, basepath)) - return NULL; - - return str; -} +#include "private.h" int nvme_namespace_filter(const struct dirent *d) { @@ -132,7 +76,7 @@ int nvme_subsys_filter(const struct dirent *d) int nvme_scan_subsystems(struct dirent ***subsys) { - _cleanup_free_ char *dir = nvme_subsys_sysfs_dir(); + const char *dir = nvme_subsys_sysfs_dir(); return scandir(dir, subsys, nvme_subsys_filter, alphasort); } @@ -145,7 +89,7 @@ int nvme_scan_subsystem_namespaces(nvme_subsystem_t s, struct dirent ***ns) int nvme_scan_ctrls(struct dirent ***ctrls) { - _cleanup_free_ char *dir = nvme_ctrl_sysfs_dir(); + const char *dir = nvme_ctrl_sysfs_dir(); return scandir(dir, ctrls, nvme_ctrls_filter, alphasort); } diff --git a/src/nvme/ioctl.c b/src/nvme/ioctl.c index 9090b7e..ce5a911 100644 --- a/src/nvme/ioctl.c +++ b/src/nvme/ioctl.c @@ -24,8 +24,7 @@ #include "ioctl.h" #include "util.h" - -static bool nvme_debug; +#include "log.h" static int nvme_verify_chr(int fd) { @@ -79,9 +78,10 @@ int nvme_get_nsid(int fd, __u32 *nsid) return -1 * (errno != 0); } -static int nvme_submit_passthru64(int fd, unsigned long ioctl_cmd, - struct nvme_passthru_cmd64 *cmd, - __u64 *result) +__attribute__((weak)) +int nvme_submit_passthru64(int fd, unsigned long ioctl_cmd, + struct nvme_passthru_cmd64 *cmd, + __u64 *result) { int err = ioctl(fd, ioctl_cmd, cmd); @@ -90,62 +90,14 @@ static int nvme_submit_passthru64(int fd, unsigned long ioctl_cmd, return err; } -static void nvme_show_command(struct nvme_passthru_cmd *cmd, int err, struct timeval start, - struct timeval end) -{ - printf("opcode : %02x\n", cmd->opcode); - printf("flags : %02x\n", cmd->flags); - printf("rsvd1 : %04x\n", cmd->rsvd1); - printf("nsid : %08x\n", cmd->nsid); - printf("cdw2 : %08x\n", cmd->cdw2); - printf("cdw3 : %08x\n", cmd->cdw3); - printf("data_len : %08x\n", cmd->data_len); - printf("metadata_len : %08x\n", cmd->metadata_len); - printf("addr : %"PRIx64"\n", (uint64_t)(uintptr_t)cmd->addr); - printf("metadata : %"PRIx64"\n", (uint64_t)(uintptr_t)cmd->metadata); - printf("cdw10 : %08x\n", cmd->cdw10); - printf("cdw11 : %08x\n", cmd->cdw11); - printf("cdw12 : %08x\n", cmd->cdw12); - printf("cdw13 : %08x\n", cmd->cdw13); - printf("cdw14 : %08x\n", cmd->cdw14); - printf("cdw15 : %08x\n", cmd->cdw15); - printf("timeout_ms : %08x\n", cmd->timeout_ms); - printf("result : %08x\n", cmd->result); - printf("err : %d\n", err); - printf("latency : %lu us\n", - (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec)); -} - -void nvme_set_debug(bool debug) -{ - nvme_debug = debug; -} - -bool nvme_get_debug(void) -{ - return nvme_debug; -} - -static int nvme_submit_passthru(int fd, unsigned long ioctl_cmd, - struct nvme_passthru_cmd *cmd, __u32 *result) +__attribute__((weak)) +int nvme_submit_passthru(int fd, unsigned long ioctl_cmd, + struct nvme_passthru_cmd *cmd, __u32 *result) { - struct timeval start; - struct timeval end; - int err; - - if (nvme_get_debug()) - gettimeofday(&start, NULL); - - err = ioctl(fd, ioctl_cmd, cmd); - - if (nvme_get_debug()) { - gettimeofday(&end, NULL); - nvme_show_command(cmd, err, start, end); - } + int err = ioctl(fd, ioctl_cmd, cmd); if (err >= 0 && result) *result = cmd->result; - return err; } @@ -246,125 +198,6 @@ int nvme_admin_passthru(int fd, __u8 opcode, __u8 flags, __u16 rsvd, metadata, timeout_ms, result); } -enum nvme_cmd_dword_fields { - NVME_DEVICE_SELF_TEST_CDW10_STC_SHIFT = 0, - NVME_DEVICE_SELF_TEST_CDW10_STC_MASK = 0xf, - NVME_DIRECTIVE_CDW11_DOPER_SHIFT = 0, - NVME_DIRECTIVE_CDW11_DTYPE_SHIFT = 8, - NVME_DIRECTIVE_CDW11_DPSEC_SHIFT = 16, - NVME_DIRECTIVE_CDW11_DOPER_MASK = 0xff, - NVME_DIRECTIVE_CDW11_DTYPE_MASK = 0xff, - NVME_DIRECTIVE_CDW11_DPSEC_MASK = 0xffff, - NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_ENDIR_SHIFT = 0, - NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_DTYPE_SHIFT = 1, - NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_ENDIR_MASK = 0x1, - NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_DTYPE_MASK = 0x1, - NVME_FW_COMMIT_CDW10_FS_SHIFT = 0, - NVME_FW_COMMIT_CDW10_CA_SHIFT = 3, - NVME_FW_COMMIT_CDW10_BPID_SHIFT = 31, - NVME_FW_COMMIT_CDW10_FS_MASK = 0x7, - NVME_FW_COMMIT_CDW10_CA_MASK = 0x7, - NVME_FW_COMMIT_CDW10_BPID_MASK = 0x1, - NVME_GET_FEATURES_CDW10_SEL_SHIFT = 8, - NVME_GET_FEATURES_CDW10_SEL_MASK = 0x7, - NVME_SET_FEATURES_CDW10_SAVE_SHIFT = 31, - NVME_SET_FEATURES_CDW10_SAVE_MASK = 0x1, - NVME_FEATURES_CDW10_FID_SHIFT = 0, - NVME_FEATURES_CDW14_UUID_SHIFT = 0, - NVME_FEATURES_CDW10_FID_MASK = 0xff, - NVME_FEATURES_CDW14_UUID_MASK = 0x7f, - NVME_LOG_CDW10_LID_SHIFT = 0, - NVME_LOG_CDW10_LSP_SHIFT = 8, - NVME_LOG_CDW10_RAE_SHIFT = 15, - NVME_LOG_CDW10_NUMDL_SHIFT = 16, - NVME_LOG_CDW11_NUMDU_SHIFT = 0, - NVME_LOG_CDW11_LSI_SHIFT = 16, - NVME_LOG_CDW14_UUID_SHIFT = 0, - NVME_LOG_CDW14_CSI_SHIFT = 24, - NVME_LOG_CDW14_OT_SHIFT = 23, - NVME_LOG_CDW10_LID_MASK = 0xff, - NVME_LOG_CDW10_LSP_MASK = 0x7f, - NVME_LOG_CDW10_RAE_MASK = 0x1, - NVME_LOG_CDW10_NUMDL_MASK = 0xffff, - NVME_LOG_CDW11_NUMDU_MASK = 0xffff, - NVME_LOG_CDW11_LSI_MASK = 0xffff, - NVME_LOG_CDW14_UUID_MASK = 0x7f, - NVME_LOG_CDW14_CSI_MASK = 0xff, - NVME_LOG_CDW14_OT_MASK = 0x1, - NVME_IDENTIFY_CDW10_CNS_SHIFT = 0, - NVME_IDENTIFY_CDW10_CNTID_SHIFT = 16, - NVME_IDENTIFY_CDW11_CNSSPECID_SHIFT = 0, - NVME_IDENTIFY_CDW14_UUID_SHIFT = 0, - NVME_IDENTIFY_CDW11_CSI_SHIFT = 24, - NVME_IDENTIFY_CDW10_CNS_MASK = 0xff, - NVME_IDENTIFY_CDW10_CNTID_MASK = 0xffff, - NVME_IDENTIFY_CDW11_CNSSPECID_MASK = 0xffff, - NVME_IDENTIFY_CDW14_UUID_MASK = 0x7f, - NVME_IDENTIFY_CDW11_CSI_MASK = 0xff, - NVME_NAMESPACE_ATTACH_CDW10_SEL_SHIFT = 0, - NVME_NAMESPACE_ATTACH_CDW10_SEL_MASK = 0xf, - NVME_NAMESPACE_MGMT_CDW10_SEL_SHIFT = 0, - NVME_NAMESPACE_MGMT_CDW10_SEL_MASK = 0xf, - NVME_NAMESPACE_MGMT_CDW11_CSI_SHIFT = 24, - NVME_NAMESPACE_MGMT_CDW11_CSI_MASK = 0xff, - NVME_VIRT_MGMT_CDW10_ACT_SHIFT = 0, - NVME_VIRT_MGMT_CDW10_RT_SHIFT = 8, - NVME_VIRT_MGMT_CDW10_CNTLID_SHIFT = 16, - NVME_VIRT_MGMT_CDW11_NR_SHIFT = 0, - NVME_VIRT_MGMT_CDW10_ACT_MASK = 0xf, - NVME_VIRT_MGMT_CDW10_RT_MASK = 0x7, - NVME_VIRT_MGMT_CDW10_CNTLID_MASK = 0xffff, - NVME_VIRT_MGMT_CDW11_NR_MASK = 0xffff, - NVME_FORMAT_CDW10_LBAF_SHIFT = 0, - NVME_FORMAT_CDW10_MSET_SHIFT = 4, - NVME_FORMAT_CDW10_PI_SHIFT = 5, - NVME_FORMAT_CDW10_PIL_SHIFT = 8, - NVME_FORMAT_CDW10_SES_SHIFT = 9, - NVME_FORMAT_CDW10_LBAFU_SHIFT = 12, - NVME_FORMAT_CDW10_LBAF_MASK = 0xf, - NVME_FORMAT_CDW10_MSET_MASK = 0x1, - NVME_FORMAT_CDW10_PI_MASK = 0x7, - NVME_FORMAT_CDW10_PIL_MASK = 0x1, - NVME_FORMAT_CDW10_SES_MASK = 0x7, - NVME_FORMAT_CDW10_LBAFU_MASK = 0x3, - NVME_SANITIZE_CDW10_SANACT_SHIFT = 0, - NVME_SANITIZE_CDW10_AUSE_SHIFT = 3, - NVME_SANITIZE_CDW10_OWPASS_SHIFT = 4, - NVME_SANITIZE_CDW10_OIPBP_SHIFT = 8, - NVME_SANITIZE_CDW10_NODAS_SHIFT = 9, - NVME_SANITIZE_CDW10_SANACT_MASK = 0x7, - NVME_SANITIZE_CDW10_AUSE_MASK = 0x1, - NVME_SANITIZE_CDW10_OWPASS_MASK = 0xf, - NVME_SANITIZE_CDW10_OIPBP_MASK = 0x1, - NVME_SANITIZE_CDW10_NODAS_MASK = 0x1, - NVME_SECURITY_NSSF_SHIFT = 0, - NVME_SECURITY_SPSP0_SHIFT = 8, - NVME_SECURITY_SPSP1_SHIFT = 16, - NVME_SECURITY_SECP_SHIFT = 24, - NVME_SECURITY_NSSF_MASK = 0xff, - NVME_SECURITY_SPSP0_MASK = 0xff, - NVME_SECURITY_SPSP1_MASK = 0xff, - NVME_SECURITY_SECP_MASK = 0xffff, - NVME_GET_LBA_STATUS_CDW13_RL_SHIFT = 0, - NVME_GET_LBA_STATUS_CDW13_ATYPE_SHIFT = 24, - NVME_GET_LBA_STATUS_CDW13_RL_MASK = 0xffff, - NVME_GET_LBA_STATUS_CDW13_ATYPE_MASK = 0xff, - NVME_ZNS_MGMT_SEND_ZSASO_SHIFT = 9, - NVME_ZNS_MGMT_SEND_ZSASO_MASK = 0x1, - NVME_ZNS_MGMT_SEND_SEL_SHIFT = 8, - NVME_ZNS_MGMT_SEND_SEL_MASK = 0x1, - NVME_ZNS_MGMT_SEND_ZSA_SHIFT = 0, - NVME_ZNS_MGMT_SEND_ZSA_MASK = 0xff, - NVME_ZNS_MGMT_RECV_ZRA_SHIFT = 0, - NVME_ZNS_MGMT_RECV_ZRA_MASK = 0xff, - NVME_ZNS_MGMT_RECV_ZRASF_SHIFT = 8, - NVME_ZNS_MGMT_RECV_ZRASF_MASK = 0xff, - NVME_ZNS_MGMT_RECV_ZRAS_FEAT_SHIFT = 16, - NVME_ZNS_MGMT_RECV_ZRAS_FEAT_MASK = 0x1, - NVME_DIM_TAS_SHIFT = 0, - NVME_DIM_TAS_MASK = 0xF, -}; - enum features { NVME_FEATURES_ARBITRATION_BURST_SHIFT = 0, NVME_FEATURES_ARBITRATION_LPW_SHIFT = 8, diff --git a/src/nvme/ioctl.h b/src/nvme/ioctl.h index 4a0698f..be4c1b7 100644 --- a/src/nvme/ioctl.h +++ b/src/nvme/ioctl.h @@ -199,6 +199,125 @@ struct nvme_uring_cmd { t + p; \ }) +enum nvme_cmd_dword_fields { + NVME_DEVICE_SELF_TEST_CDW10_STC_SHIFT = 0, + NVME_DEVICE_SELF_TEST_CDW10_STC_MASK = 0xf, + NVME_DIRECTIVE_CDW11_DOPER_SHIFT = 0, + NVME_DIRECTIVE_CDW11_DTYPE_SHIFT = 8, + NVME_DIRECTIVE_CDW11_DPSEC_SHIFT = 16, + NVME_DIRECTIVE_CDW11_DOPER_MASK = 0xff, + NVME_DIRECTIVE_CDW11_DTYPE_MASK = 0xff, + NVME_DIRECTIVE_CDW11_DPSEC_MASK = 0xffff, + NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_ENDIR_SHIFT = 0, + NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_DTYPE_SHIFT = 1, + NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_ENDIR_MASK = 0x1, + NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_DTYPE_MASK = 0x1, + NVME_FW_COMMIT_CDW10_FS_SHIFT = 0, + NVME_FW_COMMIT_CDW10_CA_SHIFT = 3, + NVME_FW_COMMIT_CDW10_BPID_SHIFT = 31, + NVME_FW_COMMIT_CDW10_FS_MASK = 0x7, + NVME_FW_COMMIT_CDW10_CA_MASK = 0x7, + NVME_FW_COMMIT_CDW10_BPID_MASK = 0x1, + NVME_GET_FEATURES_CDW10_SEL_SHIFT = 8, + NVME_GET_FEATURES_CDW10_SEL_MASK = 0x7, + NVME_SET_FEATURES_CDW10_SAVE_SHIFT = 31, + NVME_SET_FEATURES_CDW10_SAVE_MASK = 0x1, + NVME_FEATURES_CDW10_FID_SHIFT = 0, + NVME_FEATURES_CDW14_UUID_SHIFT = 0, + NVME_FEATURES_CDW10_FID_MASK = 0xff, + NVME_FEATURES_CDW14_UUID_MASK = 0x7f, + NVME_LOG_CDW10_LID_SHIFT = 0, + NVME_LOG_CDW10_LSP_SHIFT = 8, + NVME_LOG_CDW10_RAE_SHIFT = 15, + NVME_LOG_CDW10_NUMDL_SHIFT = 16, + NVME_LOG_CDW11_NUMDU_SHIFT = 0, + NVME_LOG_CDW11_LSI_SHIFT = 16, + NVME_LOG_CDW14_UUID_SHIFT = 0, + NVME_LOG_CDW14_CSI_SHIFT = 24, + NVME_LOG_CDW14_OT_SHIFT = 23, + NVME_LOG_CDW10_LID_MASK = 0xff, + NVME_LOG_CDW10_LSP_MASK = 0x7f, + NVME_LOG_CDW10_RAE_MASK = 0x1, + NVME_LOG_CDW10_NUMDL_MASK = 0xffff, + NVME_LOG_CDW11_NUMDU_MASK = 0xffff, + NVME_LOG_CDW11_LSI_MASK = 0xffff, + NVME_LOG_CDW14_UUID_MASK = 0x7f, + NVME_LOG_CDW14_CSI_MASK = 0xff, + NVME_LOG_CDW14_OT_MASK = 0x1, + NVME_IDENTIFY_CDW10_CNS_SHIFT = 0, + NVME_IDENTIFY_CDW10_CNTID_SHIFT = 16, + NVME_IDENTIFY_CDW11_CNSSPECID_SHIFT = 0, + NVME_IDENTIFY_CDW14_UUID_SHIFT = 0, + NVME_IDENTIFY_CDW11_CSI_SHIFT = 24, + NVME_IDENTIFY_CDW10_CNS_MASK = 0xff, + NVME_IDENTIFY_CDW10_CNTID_MASK = 0xffff, + NVME_IDENTIFY_CDW11_CNSSPECID_MASK = 0xffff, + NVME_IDENTIFY_CDW14_UUID_MASK = 0x7f, + NVME_IDENTIFY_CDW11_CSI_MASK = 0xff, + NVME_NAMESPACE_ATTACH_CDW10_SEL_SHIFT = 0, + NVME_NAMESPACE_ATTACH_CDW10_SEL_MASK = 0xf, + NVME_NAMESPACE_MGMT_CDW10_SEL_SHIFT = 0, + NVME_NAMESPACE_MGMT_CDW10_SEL_MASK = 0xf, + NVME_NAMESPACE_MGMT_CDW11_CSI_SHIFT = 24, + NVME_NAMESPACE_MGMT_CDW11_CSI_MASK = 0xff, + NVME_VIRT_MGMT_CDW10_ACT_SHIFT = 0, + NVME_VIRT_MGMT_CDW10_RT_SHIFT = 8, + NVME_VIRT_MGMT_CDW10_CNTLID_SHIFT = 16, + NVME_VIRT_MGMT_CDW11_NR_SHIFT = 0, + NVME_VIRT_MGMT_CDW10_ACT_MASK = 0xf, + NVME_VIRT_MGMT_CDW10_RT_MASK = 0x7, + NVME_VIRT_MGMT_CDW10_CNTLID_MASK = 0xffff, + NVME_VIRT_MGMT_CDW11_NR_MASK = 0xffff, + NVME_FORMAT_CDW10_LBAF_SHIFT = 0, + NVME_FORMAT_CDW10_MSET_SHIFT = 4, + NVME_FORMAT_CDW10_PI_SHIFT = 5, + NVME_FORMAT_CDW10_PIL_SHIFT = 8, + NVME_FORMAT_CDW10_SES_SHIFT = 9, + NVME_FORMAT_CDW10_LBAFU_SHIFT = 12, + NVME_FORMAT_CDW10_LBAF_MASK = 0xf, + NVME_FORMAT_CDW10_MSET_MASK = 0x1, + NVME_FORMAT_CDW10_PI_MASK = 0x7, + NVME_FORMAT_CDW10_PIL_MASK = 0x1, + NVME_FORMAT_CDW10_SES_MASK = 0x7, + NVME_FORMAT_CDW10_LBAFU_MASK = 0x3, + NVME_SANITIZE_CDW10_SANACT_SHIFT = 0, + NVME_SANITIZE_CDW10_AUSE_SHIFT = 3, + NVME_SANITIZE_CDW10_OWPASS_SHIFT = 4, + NVME_SANITIZE_CDW10_OIPBP_SHIFT = 8, + NVME_SANITIZE_CDW10_NODAS_SHIFT = 9, + NVME_SANITIZE_CDW10_SANACT_MASK = 0x7, + NVME_SANITIZE_CDW10_AUSE_MASK = 0x1, + NVME_SANITIZE_CDW10_OWPASS_MASK = 0xf, + NVME_SANITIZE_CDW10_OIPBP_MASK = 0x1, + NVME_SANITIZE_CDW10_NODAS_MASK = 0x1, + NVME_SECURITY_NSSF_SHIFT = 0, + NVME_SECURITY_SPSP0_SHIFT = 8, + NVME_SECURITY_SPSP1_SHIFT = 16, + NVME_SECURITY_SECP_SHIFT = 24, + NVME_SECURITY_NSSF_MASK = 0xff, + NVME_SECURITY_SPSP0_MASK = 0xff, + NVME_SECURITY_SPSP1_MASK = 0xff, + NVME_SECURITY_SECP_MASK = 0xffff, + NVME_GET_LBA_STATUS_CDW13_RL_SHIFT = 0, + NVME_GET_LBA_STATUS_CDW13_ATYPE_SHIFT = 24, + NVME_GET_LBA_STATUS_CDW13_RL_MASK = 0xffff, + NVME_GET_LBA_STATUS_CDW13_ATYPE_MASK = 0xff, + NVME_ZNS_MGMT_SEND_ZSASO_SHIFT = 9, + NVME_ZNS_MGMT_SEND_ZSASO_MASK = 0x1, + NVME_ZNS_MGMT_SEND_SEL_SHIFT = 8, + NVME_ZNS_MGMT_SEND_SEL_MASK = 0x1, + NVME_ZNS_MGMT_SEND_ZSA_SHIFT = 0, + NVME_ZNS_MGMT_SEND_ZSA_MASK = 0xff, + NVME_ZNS_MGMT_RECV_ZRA_SHIFT = 0, + NVME_ZNS_MGMT_RECV_ZRA_MASK = 0xff, + NVME_ZNS_MGMT_RECV_ZRASF_SHIFT = 8, + NVME_ZNS_MGMT_RECV_ZRASF_MASK = 0xff, + NVME_ZNS_MGMT_RECV_ZRAS_FEAT_SHIFT = 16, + NVME_ZNS_MGMT_RECV_ZRAS_FEAT_MASK = 0x1, + NVME_DIM_TAS_SHIFT = 0, + NVME_DIM_TAS_MASK = 0xF, +}; + /** * nvme_submit_admin_passthru64() - Submit a 64-bit nvme passthrough admin * command @@ -3233,9 +3352,11 @@ static inline int nvme_ns_mgmt_create(int fd, struct nvme_id_ns *ns, } /** - * nvme_ns_mgmt_delete() - Delete a non attached namespace + * nvme_ns_mgmt_delete_timeout() - Delete a non attached namespace with timeout * @fd: File descriptor of nvme device * @nsid: Namespace identifier to delete + * @timeout: Override the default timeout to this value in milliseconds; + * set to 0 to use the system default. * * It is recommended that a namespace being deleted is not attached to any * controller. Use the nvme_ns_detach_ctrls() first if the namespace is still @@ -3244,14 +3365,14 @@ static inline int nvme_ns_mgmt_create(int fd, struct nvme_id_ns *ns, * Return: The nvme command status if a response was received (see * &enum nvme_status_field) or -1 with errno set otherwise. */ -static inline int nvme_ns_mgmt_delete(int fd, __u32 nsid) +static inline int nvme_ns_mgmt_delete_timeout(int fd, __u32 nsid, __u32 timeout) { struct nvme_ns_mgmt_args args = { .result = NULL, .ns = NULL, .args_size = sizeof(args), .fd = fd, - .timeout = 0, + .timeout = timeout, .nsid = nsid, .sel = NVME_NS_MGMT_SEL_DELETE, .csi = 0, @@ -3264,6 +3385,23 @@ static inline int nvme_ns_mgmt_delete(int fd, __u32 nsid) } /** + * nvme_ns_mgmt_delete() - Delete a non attached namespace + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier to delete + * + * It is recommended that a namespace being deleted is not attached to any + * controller. Use the nvme_ns_detach_ctrls() first if the namespace is still + * attached. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_ns_mgmt_delete(int fd, __u32 nsid) +{ + return nvme_ns_mgmt_delete_timeout(fd, nsid, 0); +} + +/** * nvme_ns_attach() - Attach or detach namespace to controller(s) * @args: &struct nvme_ns_attach_args Argument structure * @@ -4047,16 +4185,4 @@ int nvme_zns_append(struct nvme_zns_append_args *args); */ int nvme_dim_send(struct nvme_dim_args *args); -/** - * nvme_set_debug - Set NVMe command debugging output - * @debug: true to enable or false to disable - */ -void nvme_set_debug(bool debug); - -/** - * nvme_get_debug - Get NVMe command debugging output - * - * Return: false if disabled or true if enabled. - */ -bool nvme_get_debug(void); #endif /* _LIBNVME_IOCTL_H */ diff --git a/src/nvme/json.c b/src/nvme/json.c index b49498a..a02bd2d 100644 --- a/src/nvme/json.c +++ b/src/nvme/json.c @@ -25,10 +25,62 @@ #define JSON_UPDATE_BOOL_OPTION(c, k, a, o) \ if (!strcmp(# a, k ) && !c->a) c->a = json_object_get_boolean(o); +static void json_import_nvme_tls_key(nvme_ctrl_t c, const char *keyring_str, + const char *encoded_key) +{ + struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c); + const char *hostnqn = nvme_host_get_hostnqn(c->s->h); + const char *subsysnqn = nvme_ctrl_get_subsysnqn(c); + int key_len; + unsigned int hmac; + long key_id; + _cleanup_free_ unsigned char *key_data = NULL; + + if (!hostnqn || !subsysnqn) { + nvme_msg(NULL, LOG_ERR, "Invalid NQNs (%s, %s)\n", + hostnqn, subsysnqn); + return; + } + key_data = nvme_import_tls_key(encoded_key, &key_len, &hmac); + if (!key_data) { + nvme_msg(NULL, LOG_ERR, "Failed to decode TLS Key '%s'\n", + encoded_key); + return; + } + key_id = nvme_insert_tls_key_versioned(keyring_str, "psk", + hostnqn, subsysnqn, + 0, hmac, key_data, key_len); + if (key_id <= 0) + nvme_msg(NULL, LOG_ERR, "Failed to insert TLS KEY, error %d\n", + errno); + else { + cfg->tls_key = key_id; + cfg->tls = true; + } +} + +static void json_export_nvme_tls_key(long keyring_id, long tls_key, + struct json_object *obj) +{ + int key_len; + _cleanup_free_ unsigned char *key_data = NULL; + + key_data = nvme_read_key(keyring_id, tls_key, &key_len); + if (key_data) { + _cleanup_free_ char *tls_str = NULL; + + tls_str = nvme_export_tls_key(key_data, key_len); + if (tls_str) + json_object_object_add(obj, "tls_key", + json_object_new_string(tls_str)); + } +} + static void json_update_attributes(nvme_ctrl_t c, struct json_object *ctrl_obj) { struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c); + const char *keyring_str = NULL, *encoded_key = NULL; json_object_object_foreach(ctrl_obj, key_str, val_obj) { JSON_UPDATE_INT_OPTION(cfg, key_str, @@ -75,21 +127,24 @@ static void json_update_attributes(nvme_ctrl_t c, if (!strcmp("keyring", key_str) && cfg->keyring == 0) { long keyring; - keyring = nvme_lookup_keyring(json_object_get_string(val_obj)); + keyring_str = json_object_get_string(val_obj); + keyring = nvme_lookup_keyring(keyring_str); if (keyring) { cfg->keyring = keyring; nvme_set_keyring(cfg->keyring); } } - if (!strcmp("tls_key", key_str) && cfg->tls_key == 0) { - long key; - - key = nvme_lookup_key("psk", - json_object_get_string(val_obj)); - if (key) - cfg->tls_key = key; - } + if (!strcmp("tls_key", key_str) && cfg->tls_key == 0) + encoded_key = json_object_get_string(val_obj); } + + /* + * We might need the keyring information from the above loop, + * so we can only import the TLS key once all entries are + * processed. + */ + if (encoded_key) + json_import_nvme_tls_key(c, keyring_str, encoded_key); } static void json_parse_port(nvme_subsystem_t s, struct json_object *port_obj) @@ -346,15 +401,11 @@ static void json_update_port(struct json_object *ctrl_array, nvme_ctrl_t c) json_object_new_string(desc)); } } - if (cfg->tls_key) { - _cleanup_free_ char *desc = - nvme_describe_key_serial(cfg->tls_key); - - if (desc) { - json_object_object_add(port_obj, "tls_key", - json_object_new_string(desc)); - } - } + /* + * Store the TLS key in PSK interchange format + */ + if (cfg->tls_key) + json_export_nvme_tls_key(cfg->keyring, cfg->tls_key, port_obj); json_object_array_add(ctrl_array, port_obj); } @@ -503,7 +554,13 @@ static void json_dump_ctrl(struct json_object *ctrl_array, nvme_ctrl_t c) JSON_BOOL_OPTION(cfg, ctrl_obj, disable_sqflow); JSON_BOOL_OPTION(cfg, ctrl_obj, hdr_digest); JSON_BOOL_OPTION(cfg, ctrl_obj, data_digest); - JSON_BOOL_OPTION(cfg, ctrl_obj, tls); + if (!strcmp(transport, "tcp")) { + JSON_BOOL_OPTION(cfg, ctrl_obj, tls); + + if (cfg->tls_key) + json_export_nvme_tls_key(cfg->keyring, cfg->tls_key, + ctrl_obj); + } JSON_BOOL_OPTION(cfg, ctrl_obj, concat); if (nvme_ctrl_is_persistent(c)) json_object_object_add(ctrl_obj, "persistent", diff --git a/src/nvme/linux.c b/src/nvme/linux.c index e29d9e7..25196fd 100644 --- a/src/nvme/linux.c +++ b/src/nvme/linux.c @@ -31,6 +31,8 @@ #ifdef CONFIG_KEYUTILS #include <keyutils.h> + +#define NVME_TLS_DEFAULT_KEYRING ".nvme" #endif #include <ccan/endian/endian.h> @@ -41,6 +43,7 @@ #include "log.h" #include "private.h" #include "base64.h" +#include "crc32.h" static int __nvme_open(const char *name) { @@ -183,7 +186,7 @@ int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, size_t max_ *size = 0; - log = malloc(xfer); + log = __nvme_alloc(xfer); if (!log) { errno = ENOMEM; return -1; @@ -236,7 +239,7 @@ int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, size_t max_ } *size = (dalb + 1) * xfer; - tmp = realloc(log, *size); + tmp = __nvme_realloc(log, *size); if (!tmp) { errno = ENOMEM; return -1; @@ -1158,6 +1161,8 @@ long nvme_lookup_keyring(const char *keyring) { key_serial_t keyring_id; + if (!keyring) + keyring = NVME_TLS_DEFAULT_KEYRING; keyring_id = find_key_by_type_and_desc("keyring", keyring, 0); if (keyring_id < 0) return 0; @@ -1187,12 +1192,119 @@ int nvme_set_keyring(long key_id) { long err; + if (key_id == 0) { + key_id = nvme_lookup_keyring(NULL); + if (key_id == 0) { + errno = ENOKEY; + return -1; + } + } + err = keyctl_link(key_id, KEY_SPEC_SESSION_KEYRING); if (err < 0) return -1; return 0; } +unsigned char *nvme_read_key(long keyring_id, long key_id, int *len) +{ + void *buffer; + int ret; + + ret = nvme_set_keyring(keyring_id); + if (ret < 0) { + errno = -ret; + return NULL; + } + ret = keyctl_read_alloc(key_id, &buffer); + if (ret < 0) { + errno = -ret; + buffer = NULL; + } else + *len = ret; + + return buffer; +} + +long nvme_update_key(long keyring_id, const char *key_type, + const char *identity, unsigned char *key_data, + int key_len) +{ + long key; + + key = keyctl_search(keyring_id, key_type, identity, 0); + if (key > 0) { + if (keyctl_revoke(key) < 0) + return 0; + } + key = add_key(key_type, identity, + key_data, key_len, keyring_id); + if (key < 0) + key = 0; + return key; +} + +struct __scan_keys_data { + nvme_scan_tls_keys_cb_t cb; + key_serial_t keyring; + void *data; +}; + +int __scan_keys_cb(key_serial_t parent, key_serial_t key, + char *desc, int desc_len, void *data) +{ + struct __scan_keys_data *d = data; + int ver, hmac, uid, gid, perm; + char type, *ptr; + + if (desc_len < 6) + return 0; + if (sscanf(desc, "psk;%d;%d;%08x;NVMe%01d%c%02d %*s", + &uid, &gid, &perm, &ver, &type, &hmac) != 6) + return 0; + /* skip key type */ + ptr = strchr(desc, ';'); + if (!ptr) + return 0; + /* skip key uid */ + ptr = strchr(ptr + 1, ';'); + if (!ptr) + return 0; + /* skip key gid */ + ptr = strchr(ptr + 1, ';'); + if (!ptr) + return 0; + /* skip key permissions */ + ptr = strchr(ptr + 1, ';'); + if (!ptr) + return 0; + /* Only use the key description for the callback */ + (d->cb)(d->keyring, key, ptr + 1, strlen(ptr) - 1, d->data); + return 1; +} + +int nvme_scan_tls_keys(const char *keyring, nvme_scan_tls_keys_cb_t cb, + void *data) +{ + struct __scan_keys_data d; + key_serial_t keyring_id = nvme_lookup_keyring(keyring); + int ret; + + if (!keyring_id) { + errno = EINVAL; + return -1; + } + ret = nvme_set_keyring(keyring_id); + if (ret < 0) + return ret; + + d.keyring = keyring_id; + d.cb = cb; + d.data = data; + ret = recursive_key_scan(keyring_id, __scan_keys_cb, &d); + return ret; +} + long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, const char *hostnqn, const char *subsysnqn, int version, int hmac, @@ -1202,21 +1314,28 @@ long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, _cleanup_free_ char *identity = NULL; size_t identity_len; _cleanup_free_ unsigned char *psk = NULL; - int ret = -1; + int ret; keyring_id = nvme_lookup_keyring(keyring); - if (keyring_id == 0) - return -1; + if (keyring_id == 0) { + errno = ENOKEY; + return 0; + } + + ret = nvme_set_keyring(keyring_id); + if (ret < 0) + return 0; identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn); if (identity_len < 0) - return -1; + return 0; identity = malloc(identity_len); if (!identity) { errno = ENOMEM; - return -1; + return 0; } + memset(identity, 0, identity_len); psk = malloc(key_len); if (!psk) { @@ -1226,19 +1345,13 @@ long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, memset(psk, 0, key_len); ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac, configured_key, psk, key_len); - if (ret != key_len) + if (ret != key_len) { + errno = ENOKEY; return 0; - - key = keyctl_search(keyring_id, key_type, identity, 0); - if (key > 0) { - if (keyctl_update(key, psk, key_len) < 0) - key = 0; - } else { - key = add_key(key_type, identity, - psk, key_len, keyring_id); - if (key < 0) - key = 0; } + + key = nvme_update_key(keyring_id, key_type, identity, + psk, key_len); return key; } @@ -1275,6 +1388,27 @@ int nvme_set_keyring(long key_id) return -1; } +unsigned char *nvme_read_key(long keyring_id, long key_id, int *len) +{ + errno = ENOTSUP; + return NULL; +} + +long nvme_update_key(long keyring_id, const char *key_type, + const char *identity, unsigned char *key_data, + int key_len) +{ + errno = ENOTSUP; + return 0; +} + +int nvme_scan_tls_keys(const char *keyring, nvme_scan_tls_keys_cb_t cb, + void *data) +{ + errno = ENOTSUP; + return -1; +} + long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, const char *hostnqn, const char *subsysnqn, int version, int hmac, @@ -1295,3 +1429,108 @@ long nvme_insert_tls_key(const char *keyring, const char *key_type, hostnqn, subsysnqn, 0, hmac, configured_key, key_len); } + +char *nvme_export_tls_key(const unsigned char *key_data, int key_len) +{ + unsigned char raw_secret[52]; + char *encoded_key; + unsigned int raw_len, encoded_len, len; + unsigned long crc = crc32(0L, NULL, 0); + + if (key_len == 32) { + raw_len = 32; + } else if (key_len == 48) { + raw_len = 48; + } else { + errno = EINVAL; + return NULL; + } + + memcpy(raw_secret, key_data, raw_len); + crc = crc32(crc, raw_secret, raw_len); + raw_secret[raw_len++] = crc & 0xff; + raw_secret[raw_len++] = (crc >> 8) & 0xff; + raw_secret[raw_len++] = (crc >> 16) & 0xff; + raw_secret[raw_len++] = (crc >> 24) & 0xff; + + encoded_len = (raw_len * 2) + 20; + encoded_key = malloc(encoded_len); + if (!encoded_key) { + errno = ENOMEM; + return NULL; + } + memset(encoded_key, 0, encoded_len); + len = sprintf(encoded_key, "NVMeTLSkey-1:%02x:", + key_len == 32 ? 1 : 2); + len += base64_encode(raw_secret, raw_len, encoded_key + len); + encoded_key[len++] = ':'; + encoded_key[len++] = '\0'; + + return encoded_key; +} + +unsigned char *nvme_import_tls_key(const char *encoded_key, int *key_len, + unsigned int *hmac) +{ + unsigned char decoded_key[128], *key_data; + unsigned int crc = crc32(0L, NULL, 0); + unsigned int key_crc; + int err, decoded_len; + + if (sscanf(encoded_key, "NVMeTLSkey-1:%02x:*s", &err) != 1) { + errno = EINVAL; + return NULL; + } + switch (err) { + case 1: + if (strlen(encoded_key) != 65) { + errno = EINVAL; + return NULL; + } + break; + case 2: + if (strlen(encoded_key) != 89) { + errno = EINVAL; + return NULL; + } + break; + default: + errno = EINVAL; + return NULL; + } + + *hmac = err; + err = base64_decode(encoded_key + 16, strlen(encoded_key) - 17, + decoded_key); + if (err < 0) { + errno = ENOKEY; + return NULL; + } + decoded_len = err; + decoded_len -= 4; + if (decoded_len != 32 && decoded_len != 48) { + errno = ENOKEY; + return NULL; + } + crc = crc32(crc, decoded_key, decoded_len); + key_crc = ((u_int32_t)decoded_key[decoded_len]) | + ((u_int32_t)decoded_key[decoded_len + 1] << 8) | + ((u_int32_t)decoded_key[decoded_len + 2] << 16) | + ((u_int32_t)decoded_key[decoded_len + 3] << 24); + if (key_crc != crc) { + nvme_msg(NULL, LOG_ERR, "CRC mismatch (key %08x, crc %08x)", + key_crc, crc); + errno = ENOKEY; + return NULL; + } + + key_data = malloc(decoded_len); + if (!key_data) { + errno = ENOMEM; + return NULL; + } + memcpy(key_data, decoded_key, decoded_len); + + *key_len = decoded_len; + return key_data; +} diff --git a/src/nvme/linux.h b/src/nvme/linux.h index 11ee76e..bd74262 100644 --- a/src/nvme/linux.h +++ b/src/nvme/linux.h @@ -274,6 +274,72 @@ long nvme_lookup_key(const char *type, const char *identity); int nvme_set_keyring(long keyring_id); /** + * nvme_read_key() - Read key raw data + * @keyring_id: Id of the keyring holding %key_id + * @key_id: Key id + * @len: Length of the returned data + * + * Links the keyring specified by @keyring_id into the session + * keyring and reads the payload of the key specified by @key_id. + * @len holds the size of the returned buffer. + * If @keyring is 0 the default keyring '.nvme' is used. + * + * Return: Pointer to the payload on success, + * or NULL with errno set otherwise. + */ +unsigned char *nvme_read_key(long keyring_id, long key_id, int *len); + +/** + * nvme_update_key() - Update key raw data + * @keyring_id: Id of the keyring holding %key_id + * @key_type: Type of the key to insert + * @identity: Key identity string + * @key_data: Raw data of the key + * @key_len: Length of @key_data + * + * Links the keyring specified by @keyring_id into the session + * keyring and updates the key reference by @identity with @key_data. + * The old key with identity @identity will be revoked to make it + * inaccessible. + * + * Return: Key id of the new key or 0 with errno set otherwise. + */ +long nvme_update_key(long keyring_id, const char *key_type, + const char *identity, unsigned char *key_data, + int key_len); + +/** + * typedef nvme_scan_tls_keys_cb_t - Callback for iterating TLS keys + * @keyring: Keyring which has been iterated + * @key: Key for which the callback has been invoked + * @desc: Description of the key + * @desc_len: Length of @desc + * @data: Pointer for caller data + * + * Called for each TLS PSK in the keyring. + */ +typedef void (*nvme_scan_tls_keys_cb_t)(long keyring, long key, + char *desc, int desc_len, void *data); + +/** + * nvme_scan_tls_keys() - Iterate over TLS keys in a keyring + * @keyring: Keyring holding TLS keys + * @cb: Callback function + * @data: Pointer for data to be passed to @cb + * + * Iterates @keyring and call @cb for each TLS key. When @keyring is NULL + * the default '.nvme' keyring is used. + * A TLS key must be of type 'psk' and the description must be of the + * form 'NVMe<0|1><R|G>0<1|2> <identity>', otherwise it will be skipped + * during iteration. + * + * Return: Number of keys for which @cb was called, or -1 with errno set + * on error. + */ +int nvme_scan_tls_keys(const char *keyring, nvme_scan_tls_keys_cb_t cb, + void *data); + +/** * nvme_insert_tls_key() - Derive and insert TLS key * @keyring: Keyring to use * @key_type: Type of the resulting key @@ -335,4 +401,68 @@ char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn, int version, int hmac, unsigned char *configured_key, int key_len); +/** + * nvme_export_tls_key() - Export a TLS key + * @key_data: Raw data of the key + * @key_len: Length of @key_data + * + * Returns @key_data in the PSK Interchange format as defined in section + * 3.6.1.5 of the NVMe TCP Transport specification. + * + * Return: The string containing the TLS identity or NULL with errno set + * on error. It is the responsibility of the caller to free the returned + * string. + */ +char *nvme_export_tls_key(const unsigned char *key_data, int key_len); + +/** + * nvme_import_tls_key() - Import a TLS key + * @encoded_key: TLS key in PSK interchange format + * @key_len: Length of the resulting key data + * @hmac: HMAC algorithm + * + * Imports @key_data in the PSK Interchange format as defined in section + * 3.6.1.5 of the NVMe TCP Transport specification. + * + * Return: The raw data of the PSK or NULL with errno set on error. It is + * the responsibility of the caller to free the returned string. + */ +unsigned char *nvme_import_tls_key(const char *encoded_key, int *key_len, + unsigned int *hmac); + +/** + * nvme_submit_passthru - Low level ioctl wrapper for passthru commands + * @fd: File descriptor of the nvme device + * @ioctl_cmd: IOCTL command id + * @cmd: Passhtru command + * @result: Optional field to return the result + * + * This is a low level library function which should not be used directly. It is + * exposed as weak symbol so that the user application is able to provide their own + * implementation of this function with additional debugging or logging code. + * + * Return: The value from the ioctl system call (see ioctl documentation) + */ +__attribute__((weak)) +int nvme_submit_passthru(int fd, unsigned long ioctl_cmd, + struct nvme_passthru_cmd *cmd, __u32 *result); + +/** + * nvme_submit_passthru64 - Low level ioctl wrapper for passthru commands + * @fd: File descriptor of the nvme device + * @ioctl_cmd: IOCTL command id + * @cmd: Passhtru command + * @result: Optional field to return the result + * + * This is a low level library function which should not be used directly. It is + * exposed as weak symbol so that the user application is able to provide their own + * implementation of this function with additional debugging or logging code. + * + * Return: The value from the ioctl system call (see ioctl documentation) + */ +__attribute__((weak)) +int nvme_submit_passthru64(int fd, unsigned long ioctl_cmd, + struct nvme_passthru_cmd64 *cmd, + __u64 *result); + #endif /* _LIBNVME_LINUX_H */ diff --git a/src/nvme/log.c b/src/nvme/log.c index 2ffca3e..c98d213 100644 --- a/src/nvme/log.c +++ b/src/nvme/log.c @@ -58,6 +58,8 @@ __nvme_msg(nvme_root_t r, int lvl, if (r && lvl > r->log_level) return; + if (!r && lvl > DEFAULT_LOGLEVEL) + return; if (r && r->log_timestamp) { struct timespec now; @@ -99,7 +101,30 @@ void nvme_init_logging(nvme_root_t r, int lvl, bool log_pid, bool log_tstamp) r->log_timestamp = log_tstamp; } +int nvme_get_logging_level(nvme_root_t r, bool *log_pid, bool *log_tstamp) +{ + if (!r) + r = root; + if (!r) + return DEFAULT_LOGLEVEL; + if (log_pid) + *log_pid = r->log_pid; + if (log_tstamp) + *log_tstamp = r->log_timestamp; + return r->log_level; +} + void nvme_set_root(nvme_root_t r) { root = r; } + +void nvme_set_debug(bool debug) +{ + root->log_level = debug ? LOG_DEBUG : DEFAULT_LOGLEVEL; +} + +bool nvme_get_debug(void) +{ + return root->log_level == LOG_DEBUG; +} diff --git a/src/nvme/log.h b/src/nvme/log.h index 7c345f6..cd243ea 100644 --- a/src/nvme/log.h +++ b/src/nvme/log.h @@ -36,6 +36,20 @@ void nvme_init_logging(nvme_root_t r, int lvl, bool log_pid, bool log_tstamp); /** + * nvme_get_logging_level() - Get current logging level + * @r: nvme_root_t context + * @log_pid: Pointer to store a current value of logging of + * the PID flag at (optional). + * @log_tstamp: Pointer to store a current value of logging of + * the timestamp flag at (optional). + * + * Retrieves current values of logging variables. + * + * Return: current log level value or DEFAULT_LOGLEVEL if not initialized. + */ +int nvme_get_logging_level(nvme_root_t r, bool *log_pid, bool *log_tstamp); + +/** * nvme_set_root() - Set nvme_root_t context * @r: nvme_root_t context * @@ -48,4 +62,21 @@ void nvme_init_logging(nvme_root_t r, int lvl, bool log_pid, bool log_tstamp); */ void nvme_set_root(nvme_root_t r); +/** + * nvme_set_debug - Set NVMe command debugging output + * @debug: true to enable or false to disable + * + * Don't use it, it's debricated. + */ +void nvme_set_debug(bool debug); + +/** + * nvme_get_debug - Get NVMe command debugging output + * + * Don't use it, it's debricated. + * + * Return: false if disabled or true if enabled. + */ +bool nvme_get_debug(void); + #endif /* _LOG_H */ diff --git a/src/nvme/nbft.c b/src/nvme/nbft.c index f2ffc21..b7d0dc8 100644 --- a/src/nvme/nbft.c +++ b/src/nvme/nbft.c @@ -246,6 +246,12 @@ static int read_ssns(struct nbft_info *nbft, ssns->nid_type = raw_ssns->nidt; ssns->nid = raw_ssns->nid; + /* flags */ + ssns->unavailable = !!(le16_to_cpu(raw_ssns->flags) & + NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL); + ssns->discovered = !!(le16_to_cpu(raw_ssns->flags) & + NBFT_SSNS_DISCOVERED_NAMESPACE); + /* security profile */ if (raw_ssns->security_desc_index) { ssns->security = security_from_index(nbft, raw_ssns->security_desc_index); diff --git a/src/nvme/nbft.h b/src/nvme/nbft.h index 6012e16..a9eaf6f 100644 --- a/src/nvme/nbft.h +++ b/src/nvme/nbft.h @@ -12,6 +12,14 @@ #include <sys/types.h> #include "util.h" +/** + * DOC: nbft.h + * + * NVM Express Boot Specification, Revision 1.0 + * + * Note: this API is currently unstable, subject to further additions. + */ + /* * ACPI NBFT table structures (TP8012 Boot Specification rev. 1.0) */ @@ -994,17 +1002,17 @@ enum nbft_discovery_flags { /** * enum nbft_info_primary_admin_host_flag - Primary Administrative Host Descriptor Flags * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED: Not Indicated by Driver: The driver - * that created this NBFT provided no - * administrative priority hint for - * this NBFT. + * that created this NBFT provided no + * administrative priority hint for + * this NBFT. * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED: Unselected: The driver that created - * this NBFT explicitly indicated that - * this NBFT should not be prioritized - * over any other NBFT. + * this NBFT explicitly indicated that + * this NBFT should not be prioritized + * over any other NBFT. * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED: Selected: The driver that created - * this NBFT explicitly indicated that - * this NBFT should be prioritized over - * any other NBFT. + * this NBFT explicitly indicated that + * this NBFT should be prioritized over + * any other NBFT. * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED: Reserved. */ enum nbft_info_primary_admin_host_flag { @@ -1019,13 +1027,13 @@ enum nbft_info_primary_admin_host_flag { * @id: Host ID (raw UUID, length = 16 bytes). * @nqn: Host NQN. * @host_id_configured: HostID Configured Flag: value of True indicates that @id - * contains administratively-configured value, or driver - * default value if False. + * contains administratively-configured value, or driver + * default value if False. * @host_nqn_configured: Host NQN Configured Flag: value of True indicates that - * @nqn contains administratively-configured value, - * or driver default value if False. + * @nqn contains administratively-configured value, + * or driver default value if False. * @primary: Primary Administrative Host Descriptor, see - * &enum nbft_info_primary_admin_host_flag. + * &enum nbft_info_primary_admin_host_flag. */ struct nbft_info_host { unsigned char *id; @@ -1039,34 +1047,34 @@ struct nbft_info_host { * struct nbft_info_hfi_info_tcp - HFI Transport Info Descriptor - NVMe/TCP * @pci_sbdf: PCI Express Routing ID for the HFI Transport Function. * @mac_addr: MAC Address: The MAC address of this HFI, - * in EUI-48TM format. + * in EUI-48TM format. * @vlan: The VLAN identifier if the VLAN is associated with - * this HFI, as defined in IEEE 802.1q-2018 or zeroes - * if no VLAN is associated with this HFI. + * this HFI, as defined in IEEE 802.1q-2018 or zeroes + * if no VLAN is associated with this HFI. * @ip_origin: The source of Ethernet L3 configuration information - * used by the driver or 0 if not used. + * used by the driver or 0 if not used. * @ipaddr: The IPv4 or IPv6 address of this HFI. * @subnet_mask_prefix: The IPv4 or IPv6 subnet mask in CIDR routing prefix - * notation. + * notation. * @gateway_ipaddr: The IPv4 or IPv6 address of the IP gateway for this - * HFI or zeroes if no IP gateway is specified. + * HFI or zeroes if no IP gateway is specified. * @route_metric: The cost value for the route indicated by this HFI. * @primary_dns_ipaddr: The IPv4 or IPv6 address of the Primary DNS server - * for this HFI. + * for this HFI. * @secondary_dns_ipaddr: The IPv4 or IPv6 address of the Secondary DNS server - * for this HFI. + * for this HFI. * @dhcp_server_ipaddr: The IPv4 or IPv6 address of the DHCP server used - * to assign this HFI address. + * to assign this HFI address. * @host_name: The Host Name string. * @this_hfi_is_default_route: If True, then the BIOS utilized this interface - * described by HFI to be the default route with highest - * priority. If False, then routes are local to their - * own scope. + * described by HFI to be the default route with highest + * priority. If False, then routes are local to their + * own scope. * @dhcp_override: If True, then HFI information was populated - * by consuming the DHCP on this interface. If False, - * then the HFI information was set administratively - * by a configuration interface to the driver and - * pre-OS envrionment. + * by consuming the DHCP on this interface. If False, + * then the HFI information was set administratively + * by a configuration interface to the driver and + * pre-OS envrionment. */ struct nbft_info_hfi_info_tcp { __u32 pci_sbdf; @@ -1088,7 +1096,7 @@ struct nbft_info_hfi_info_tcp { /** * struct nbft_info_hfi - Host Fabric Interface (HFI) Descriptor * @index: HFI Descriptor Index: indicates the number of this HFI Descriptor - * in the Host Fabric Interface Descriptor List. + * in the Host Fabric Interface Descriptor List. * @transport: Transport Type string (e.g. 'tcp'). * @tcp_info: The HFI Transport Info Descriptor, see &struct nbft_info_hfi_info_tcp. */ @@ -1101,12 +1109,12 @@ struct nbft_info_hfi { /** * struct nbft_info_discovery - Discovery Descriptor * @index: The number of this Discovery Descriptor in the Discovery - * Descriptor List. + * Descriptor List. * @security: The Security Profile Descriptor, see &struct nbft_info_security. * @hfi: The HFI Descriptor associated with this Discovery Descriptor. - * See &struct nbft_info_hfi. + * See &struct nbft_info_hfi. * @uri: A URI which indicates an NVMe Discovery controller associated - * with this Discovery Descriptor. + * with this Discovery Descriptor. * @nqn: An NVMe Discovery controller NQN. */ struct nbft_info_discovery { @@ -1120,7 +1128,7 @@ struct nbft_info_discovery { /** * struct nbft_info_security - Security Profile Descriptor * @index: The number of this Security Profile Descriptor in the Security - * Profile Descriptor List. + * Profile Descriptor List. */ struct nbft_info_security { int index; @@ -1143,35 +1151,39 @@ enum nbft_info_nid_type { /** * struct nbft_info_subsystem_ns - Subsystem Namespace (SSNS) info - * @index: SSNS Descriptor Index in the descriptor list. + * @index: SSNS Descriptor Index in the descriptor list. * @discovery: Primary Discovery Controller associated with - * this SSNS Descriptor. + * this SSNS Descriptor. * @security: Security Profile Descriptor associated with - * this namespace. + * this namespace. * @num_hfis: Number of HFIs. * @hfis: List of HFIs associated with this namespace. - * Includes the primary HFI at the first position - * and all secondary HFIs. This array is null-terminated. + * Includes the primary HFI at the first position + * and all secondary HFIs. This array is null-terminated. * @transport: Transport Type string (e.g. 'tcp'). * @traddr: Subsystem Transport Address. * @trsvcid: Subsystem Transport Service Identifier. * @subsys_port_id: The Subsystem Port ID. * @nsid: The Namespace ID of this descriptor or when @nid - * should be used instead. + * should be used instead. * @nid_type: Namespace Identifier Type, see &enum nbft_info_nid_type. * @nid: The Namespace Identifier value. * @subsys_nqn: Subsystem and Namespace NQN. * @pdu_header_digest_required: PDU Header Digest (HDGST) Flag: the use of NVM Header - * Digest Enabled is required. - * @data_digest_required: Data Digest (DDGST) Flag: the use of NVM Data Digest - * Enabled is required. + * Digest Enabled is required. + * @data_digest_required: Data Digest (DDGST) Flag: the use of NVM Data Digest + * Enabled is required. * @controller_id: Controller ID (SSNS Extended Information Descriptor): - * The controller ID associated with the Admin Queue - * or 0 if not supported. + * The controller ID associated with the Admin Queue + * or 0 if not supported. * @asqsz: Admin Submission Queue Size (SSNS Extended Information - * Descriptor) or 0 if not supported. + * Descriptor) or 0 if not supported. * @dhcp_root_path_string: DHCP Root Path Override string (SSNS Extended - * Information Descriptor). + * Information Descriptor). + * @discovered: Indicates that this namespace was acquired + * through discovery. + * @unavailable: Namespace is unavailable as indicated by + * the pre-OS driver. */ struct nbft_info_subsystem_ns { int index; @@ -1192,6 +1204,8 @@ struct nbft_info_subsystem_ns { int controller_id; int asqsz; char *dhcp_root_path_string; + bool discovered; + bool unavailable; }; /** diff --git a/src/nvme/private.h b/src/nvme/private.h index 3505802..dec3d85 100644 --- a/src/nvme/private.h +++ b/src/nvme/private.h @@ -17,9 +17,12 @@ #include "mi.h" -char *nvme_ctrl_sysfs_dir(void); -char *nvme_subsys_sysfs_dir(void); -char *nvme_ns_sysfs_dir(void); +const char *nvme_subsys_sysfs_dir(void); +const char *nvme_ctrl_sysfs_dir(void); +const char *nvme_ns_sysfs_dir(void); +const char *nvme_slots_sysfs_dir(void); +const char *nvme_uuid_ibm_filename(void); +const char *nvme_dmi_entries_dir(void); struct nvme_path { struct list_node entry; @@ -83,6 +86,7 @@ struct nvme_ctrl { char *dhchap_key; char *dhchap_ctrl_key; char *cntrltype; + char *cntlid; char *dctype; char *phy_slot; bool discovery_ctrl; @@ -185,6 +189,8 @@ nvme_ctrl_t __nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport, void *__nvme_alloc(size_t len); +void *__nvme_realloc(void *p, size_t len); + #if (LOG_FUNCNAME == 1) #define __nvme_log_func __func__ #else @@ -286,4 +292,7 @@ struct __mi_mctp_socket_ops { }; void __nvme_mi_mctp_set_ops(const struct __mi_mctp_socket_ops *newops); +#define SECTOR_SIZE 512 +#define SECTOR_SHIFT 9 + #endif /* _LIBNVME_PRIVATE_H */ diff --git a/src/nvme/sysfs.c b/src/nvme/sysfs.c new file mode 100644 index 0000000..ea4f0e2 --- /dev/null +++ b/src/nvme/sysfs.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +#include <stdio.h> +#include <stdlib.h> + +#include "private.h" + +#define PATH_UUID_IBM "/proc/device-tree/ibm,partition-uuid" +#define PATH_SYSFS_BLOCK "/sys/block" +#define PATH_SYSFS_SLOTS "/sys/bus/pci/slots" +#define PATH_SYSFS_NVME_SUBSYSTEM "/sys/class/nvme-subsystem" +#define PATH_SYSFS_NVME "/sys/class/nvme" +#define PATH_DMI_ENTRIES "/sys/firmware/dmi/entries" + +static const char *make_sysfs_dir(const char *path) +{ + char *basepath = getenv("LIBNVME_SYSFS_PATH"); + char *str; + + if (!basepath) + return path; + + if (asprintf(&str, "%s%s", basepath, path) < 0) + return NULL; + + return str; +} + +const char *nvme_subsys_sysfs_dir(void) +{ + static const char *str; + + if (str) + return str; + + return str = make_sysfs_dir(PATH_SYSFS_NVME_SUBSYSTEM); +} + +const char *nvme_ctrl_sysfs_dir(void) +{ + static const char *str; + + if (str) + return str; + + return str = make_sysfs_dir(PATH_SYSFS_NVME); +} + +const char *nvme_ns_sysfs_dir(void) +{ + static const char *str; + + if (str) + return str; + + return str = make_sysfs_dir(PATH_SYSFS_BLOCK); +} + +const char *nvme_slots_sysfs_dir(void) +{ + static const char *str; + + if (str) + return str; + + return str = make_sysfs_dir(PATH_SYSFS_SLOTS); +} + +const char *nvme_uuid_ibm_filename(void) +{ + static const char *str; + + if (str) + return str; + + return str = make_sysfs_dir(PATH_UUID_IBM); +} + +const char *nvme_dmi_entries_dir(void) +{ + static const char *str; + + if (str) + return str; + + return str = make_sysfs_dir(PATH_DMI_ENTRIES); +} diff --git a/src/nvme/tree.c b/src/nvme/tree.c index 344f8bc..eb9486d 100644 --- a/src/nvme/tree.c +++ b/src/nvme/tree.c @@ -61,22 +61,6 @@ struct candidate_args { }; typedef bool (*ctrl_match_t)(struct nvme_ctrl *c, struct candidate_args *candidate); -#define PATH_SYSFS_SLOTS "/sys/bus/pci/slots" - -static char *nvme_slots_sysfs_dir(void) -{ - char *basepath = getenv("LIBNVME_SYSFS_PATH"); - char *str; - - if (!basepath) - return strdup(PATH_SYSFS_SLOTS); - - if (!asprintf(&str, "%s" PATH_SYSFS_SLOTS, basepath)) - return NULL; - - return str; -} - static struct nvme_host *default_host; static void __nvme_free_host(nvme_host_t h); @@ -672,10 +656,9 @@ static int nvme_subsystem_scan_namespaces(nvme_root_t r, nvme_subsystem_t s, static int nvme_init_subsystem(nvme_subsystem_t s, const char *name) { - _cleanup_free_ char *subsys_dir = nvme_subsys_sysfs_dir(); char *path; - if (asprintf(&path, "%s/%s", subsys_dir, name) < 0) + if (asprintf(&path, "%s/%s", nvme_subsys_sysfs_dir(), name) < 0) return -1; s->model = nvme_get_attr(path, "model"); @@ -716,12 +699,11 @@ static int nvme_scan_subsystem(struct nvme_root *r, const char *name, { struct nvme_subsystem *s = NULL, *_s; _cleanup_free_ char *path = NULL, *subsysnqn = NULL; - _cleanup_free_ char *subsys_dir = nvme_subsys_sysfs_dir(); nvme_host_t h = NULL; int ret; nvme_msg(r, LOG_DEBUG, "scan subsystem %s\n", name); - ret = asprintf(&path, "%s/%s", subsys_dir, name); + ret = asprintf(&path, "%s/%s", nvme_subsys_sysfs_dir(), name); if (ret < 0) return ret; @@ -1026,6 +1008,11 @@ const char *nvme_ctrl_get_dhchap_host_key(nvme_ctrl_t c) return c->dhchap_key; } +const char *nvme_ctrl_get_cntlid(nvme_ctrl_t c) +{ + return c->cntlid; +} + void nvme_ctrl_set_dhchap_host_key(nvme_ctrl_t c, const char *key) { if (c->dhchap_key) { @@ -1135,6 +1122,7 @@ void nvme_deconfigure_ctrl(nvme_ctrl_t c) FREE_CTRL_ATTR(c->address); FREE_CTRL_ATTR(c->dctype); FREE_CTRL_ATTR(c->cntrltype); + FREE_CTRL_ATTR(c->cntlid); FREE_CTRL_ATTR(c->phy_slot); } @@ -1703,7 +1691,7 @@ static int nvme_ctrl_scan_namespaces(nvme_root_t r, struct nvme_ctrl *c) static char *nvme_ctrl_lookup_subsystem_name(nvme_root_t r, const char *ctrl_name) { - _cleanup_free_ char *subsys_dir = nvme_subsys_sysfs_dir(); + const char *subsys_dir = nvme_subsys_sysfs_dir(); _cleanup_dirents_ struct dirents subsys = {}; int i; @@ -1730,7 +1718,7 @@ static char *nvme_ctrl_lookup_subsystem_name(nvme_root_t r, static char *nvme_ctrl_lookup_phy_slot(nvme_root_t r, const char *address) { - _cleanup_free_ char *slots_sysfs_dir = nvme_slots_sysfs_dir(); + const char *slots_sysfs_dir = nvme_slots_sysfs_dir(); _cleanup_free_ char *target_addr = NULL; _cleanup_dir_ DIR *slots_dir = NULL; int ret; @@ -1747,7 +1735,7 @@ static char *nvme_ctrl_lookup_phy_slot(nvme_root_t r, const char *address) } target_addr = strndup(address, 10); - while (!(entry = readdir(slots_dir))) { + while ((entry = readdir(slots_dir))) { if (entry->d_type == DT_DIR && strncmp(entry->d_name, ".", 1) != 0 && strncmp(entry->d_name, "..", 2) != 0) { @@ -1772,7 +1760,7 @@ static int nvme_configure_ctrl(nvme_root_t r, nvme_ctrl_t c, const char *path, const char *name) { DIR *d; - char *host_key; + char *host_key, *tls_psk; d = opendir(path); if (!d) { @@ -1807,7 +1795,18 @@ static int nvme_configure_ctrl(nvme_root_t r, nvme_ctrl_t c, const char *path, free(c->dhchap_ctrl_key); c->dhchap_ctrl_key = NULL; } + tls_psk = nvme_get_ctrl_attr(c, "tls_key"); + if (tls_psk) { + char *endptr; + long key_id = strtol(tls_psk, &endptr, 16); + + if (endptr != tls_psk) { + c->cfg.tls_key = key_id; + c->cfg.tls = true; + } + } c->cntrltype = nvme_get_ctrl_attr(c, "cntrltype"); + c->cntlid = nvme_get_ctrl_attr(c, "cntlid"); c->dctype = nvme_get_ctrl_attr(c, "dctype"); c->phy_slot = nvme_ctrl_lookup_phy_slot(r, c->address); @@ -1817,7 +1816,6 @@ static int nvme_configure_ctrl(nvme_root_t r, nvme_ctrl_t c, const char *path, int nvme_init_ctrl(nvme_host_t h, nvme_ctrl_t c, int instance) { - _cleanup_free_ char *ctrl_dir = nvme_ctrl_sysfs_dir(); _cleanup_free_ char *subsys_name = NULL; _cleanup_free_ char *name = NULL; nvme_subsystem_t s; @@ -1829,7 +1827,7 @@ int nvme_init_ctrl(nvme_host_t h, nvme_ctrl_t c, int instance) errno = ENOMEM; return -1; } - ret = asprintf(&path, "%s/nvme%d", ctrl_dir, instance); + ret = asprintf(&path, "%s/%s", nvme_ctrl_sysfs_dir(), name); if (ret < 0) { errno = ENOMEM; return ret; @@ -1872,7 +1870,7 @@ static nvme_ctrl_t nvme_ctrl_alloc(nvme_root_t r, nvme_subsystem_t s, { nvme_ctrl_t c, p; _cleanup_free_ char *addr = NULL, *address = NULL; - char *a, *e; + char *a = NULL, *e = NULL; _cleanup_free_ char *transport = NULL; char *traddr = NULL, *trsvcid = NULL; char *host_traddr = NULL, *host_iface = NULL; @@ -1973,11 +1971,10 @@ nvme_ctrl_t nvme_scan_ctrl(nvme_root_t r, const char *name) _cleanup_free_ char *path = NULL; _cleanup_free_ char *hostnqn = NULL, *hostid = NULL; _cleanup_free_ char *subsysnqn = NULL, *subsysname = NULL; - _cleanup_free_ char *ctrl_dir = nvme_ctrl_sysfs_dir(); int ret; nvme_msg(r, LOG_DEBUG, "scan controller %s\n", name); - ret = asprintf(&path, "%s/%s", ctrl_dir, name); + ret = asprintf(&path, "%s/%s", nvme_ctrl_sysfs_dir(), name); if (ret < 0) { errno = ENOMEM; return NULL; @@ -2452,12 +2449,13 @@ static int nvme_ns_init(const char *path, struct nvme_ns *ns) { _cleanup_free_ char *attr = NULL; struct stat sb; + uint64_t size; int ret; struct sysfs_attr_table base[] = { { &ns->nsid, nvme_strtou32, true, "nsid" }, - { &ns->lba_count, nvme_strtou64, true, "size" }, - { &ns->lba_size, nvme_strtou64, true, "queue/logical_block_size" }, + { &size, nvme_strtou64, true, "size" }, + { &ns->lba_size, nvme_strtou32, true, "queue/logical_block_size" }, { ns->eui64, nvme_strtoeuid, false, "eui" }, { ns->nguid, nvme_strtouuid, false, "nguid" }, { ns->uuid, nvme_strtouuid, false, "uuid" } @@ -2468,6 +2466,11 @@ static int nvme_ns_init(const char *path, struct nvme_ns *ns) return ret; ns->lba_shift = GETSHIFT(ns->lba_size); + /* + * size is in 512 bytes units and lba_count is in lba_size which are not + * necessarily the same. + */ + ns->lba_count = size >> (ns->lba_shift - SECTOR_SHIFT); if (asprintf(&attr, "%s/csi", path) < 0) return -errno; @@ -2485,7 +2488,7 @@ static int nvme_ns_init(const char *path, struct nvme_ns *ns) if (ret) return ret; } else { - struct nvme_id_ns *id; + _cleanup_free_ struct nvme_id_ns *id = NULL; uint8_t flbas; id = __nvme_alloc(sizeof(*ns)); @@ -2605,9 +2608,7 @@ static struct nvme_ns *__nvme_scan_namespace(const char *sysfs_dir, const char * nvme_ns_t nvme_scan_namespace(const char *name) { - _cleanup_free_ char *ns_dir = nvme_ns_sysfs_dir(); - - return __nvme_scan_namespace(ns_dir, name); + return __nvme_scan_namespace(nvme_ns_sysfs_dir(), name); } static int nvme_ctrl_scan_namespace(nvme_root_t r, struct nvme_ctrl *c, diff --git a/src/nvme/tree.h b/src/nvme/tree.h index a30e8eb..5e82579 100644 --- a/src/nvme/tree.h +++ b/src/nvme/tree.h @@ -1026,6 +1026,14 @@ const char *nvme_ctrl_get_host_iface(nvme_ctrl_t c); const char *nvme_ctrl_get_dhchap_host_key(nvme_ctrl_t c); /** + * nvme_ctrl_get_cntlid() - Controller id + * @c: Controller to be checked + * + * Return : Controller id of @c + */ +const char *nvme_ctrl_get_cntlid(nvme_ctrl_t c); + +/** * nvme_ctrl_set_dhchap_host_key() - Set host key * @c: Host for which the key should be set * @key: DH-HMAC-CHAP Key to set or NULL to clear existing key diff --git a/src/nvme/types.h b/src/nvme/types.h index fe79b6e..26e5e25 100644 --- a/src/nvme/types.h +++ b/src/nvme/types.h @@ -230,6 +230,50 @@ static inline bool nvme_is_64bit_reg(__u32 offset) } } +/** + * enum nvme_cap - This field indicates the controller capabilities register + * @NVME_CAP_MQES_SHIFT: Shift amount to get the maximum queue entries supported + * @NVME_CAP_CQR_SHIFT: Shift amount to get the contiguous queues required + * @NVME_CAP_AMS_SHIFT: Shift amount to get the arbitration mechanism supported + * @NVME_CAP_TO_SHIFT: Shift amount to get the timeout + * @NVME_CAP_DSTRD_SHIFT: Shift amount to get the doorbell stride + * @NVME_CAP_NSSRC_SHIFT: Shift amount to get the NVM subsystem reset supported + * @NVME_CAP_CSS_SHIFT: Shift amount to get the command sets supported + * @NVME_CAP_BPS_SHIFT: Shift amount to get the boot partition support + * @NVME_CAP_CPS_SHIFT: Shift amount to get the controller power scope + * @NVME_CAP_MPSMIN_SHIFT: Shift amount to get the memory page size minimum + * @NVME_CAP_MPSMAX_SHIFT: Shift amount to get the memory page size maximum + * @NVME_CAP_PMRS_SHIFT: Shift amount to get the persistent memory region supported + * @NVME_CAP_CMBS_SHIFT: Shift amount to get the controller memory buffer supported + * @NVME_CAP_NSSS_SHIFT: Shift amount to get the NVM subsystem shutdown supported + * @NVME_CAP_CRMS_SHIFT: Shift amount to get the controller ready modes supported + * @NVME_CAP_MQES_MASK: Mask to get the maximum queue entries supported + * @NVME_CAP_CQR_MASK: Mask to get the contiguous queues required + * @NVME_CAP_AMS_MASK: Mask to get the arbitration mechanism supported + * @NVME_CAP_TO_MASK: Mask to get the timeout + * @NVME_CAP_DSTRD_MASK: Mask to get the doorbell stride + * @NVME_CAP_NSSRC_MASK: Mask to get the NVM subsystem reset supported + * @NVME_CAP_CSS_MASK: Mask to get the command sets supported + * @NVME_CAP_BPS_MASK: Mask to get the boot partition support + * @NVME_CAP_CPS_MASK: Mask to get the controller power scope + * @NVME_CAP_MPSMIN_MASK: Mask to get the memory page size minimum + * @NVME_CAP_MPSMAX_MASK: Mask to get the memory page size maximum + * @NVME_CAP_PMRS_MASK: Mask to get the persistent memory region supported + * @NVME_CAP_CMBS_MASK: Mask to get the controller memory buffer supported + * @NVME_CAP_NSSS_MASK: Mask to get the NVM subsystem shutdown supported + * @NVME_CAP_CRMS_MASK: Mask to get the controller ready modes supported + * @NVME_CAP_AMS_WRR: Weighted round robin with urgent priority class + * @NVME_CAP_AMS_VS: Vendor specific + * @NVME_CAP_CSS_NVM: NVM command set or a discovery controller + * @NVME_CAP_CSS_CSI: Controller supports one or more I/O command sets + * @NVME_CAP_CSS_ADMIN: No I/O command set is supported + * @NVME_CAP_CPS_NONE: Not reported + * @NVME_CAP_CPS_CTRL: Controller scope + * @NVME_CAP_CPS_DOMAIN: Domain scope + * @NVME_CAP_CPS_NVMS: NVM subsystem scope + * @NVME_CAP_CRWMS: Controller ready with media support + * @NVME_CAP_CRIMS: Controller ready independent of media support + */ enum nvme_cap { NVME_CAP_MQES_SHIFT = 0, NVME_CAP_CQR_SHIFT = 16, @@ -239,10 +283,12 @@ enum nvme_cap { NVME_CAP_NSSRC_SHIFT = 36, NVME_CAP_CSS_SHIFT = 37, NVME_CAP_BPS_SHIFT = 45, + NVME_CAP_CPS_SHIFT = 46, NVME_CAP_MPSMIN_SHIFT = 48, NVME_CAP_MPSMAX_SHIFT = 52, NVME_CAP_PMRS_SHIFT = 56, NVME_CAP_CMBS_SHIFT = 57, + NVME_CAP_NSSS_SHIFT = 58, NVME_CAP_CRMS_SHIFT = 59, NVME_CAP_MQES_MASK = 0xffff, NVME_CAP_CQR_MASK = 0x1, @@ -252,16 +298,22 @@ enum nvme_cap { NVME_CAP_NSSRC_MASK = 0x1, NVME_CAP_CSS_MASK = 0xff, NVME_CAP_BPS_MASK = 0x1, + NVME_CAP_CPS_MASK = 0x3, NVME_CAP_MPSMIN_MASK = 0xf, NVME_CAP_MPSMAX_MASK = 0xf, NVME_CAP_PMRS_MASK = 0x1, NVME_CAP_CMBS_MASK = 0x1, + NVME_CAP_NSSS_MASK = 0x1, NVME_CAP_CRMS_MASK = 0x3, NVME_CAP_AMS_WRR = 1 << 0, NVME_CAP_AMS_VS = 1 << 1, NVME_CAP_CSS_NVM = 1 << 0, NVME_CAP_CSS_CSI = 1 << 6, NVME_CAP_CSS_ADMIN = 1 << 7, + NVME_CAP_CPS_NONE = 0, + NVME_CAP_CPS_CTRL = 1, + NVME_CAP_CPS_DOMAIN = 2, + NVME_CAP_CPS_NVMS = 3, NVME_CAP_CRWMS = 1 << 0, NVME_CAP_CRIMS = 1 << 1, }; @@ -274,12 +326,23 @@ enum nvme_cap { #define NVME_CAP_NSSRC(cap) NVME_GET(cap, CAP_NSSRC) #define NVME_CAP_CSS(cap) NVME_GET(cap, CAP_CSS) #define NVME_CAP_BPS(cap) NVME_GET(cap, CAP_BPS) +#define NVME_CAP_CPS(cap) NVME_GET(cap, CAP_CPS) #define NVME_CAP_MPSMIN(cap) NVME_GET(cap, CAP_MPSMIN) #define NVME_CAP_MPSMAX(cap) NVME_GET(cap, CAP_MPSMAX) #define NVME_CAP_PMRS(cap) NVME_GET(cap, CAP_PMRS) #define NVME_CAP_CMBS(cap) NVME_GET(cap, CAP_CMBS) +#define NVME_CAP_NSSS(cap) NVME_GET(cap, CAP_NSSS) #define NVME_CAP_CRMS(cap) NVME_GET(cap, CAP_CRMS) +/** + * enum nvme_vs - This field indicates the version + * @NVME_VS_TER_SHIFT: Shift amount to get the tertiary version + * @NVME_VS_MNR_SHIFT: Shift amount to get the minor version + * @NVME_VS_MJR_SHIFT: Shift amount to get the major version + * @NVME_VS_TER_MASK: Mask to get the tertiary version + * @NVME_VS_MNR_MASK: Mask to get the minor version + * @NVME_VS_MJR_MASK: Mask to get the major version + */ enum nvme_vs { NVME_VS_TER_SHIFT = 0, NVME_VS_MNR_SHIFT = 8, @@ -297,6 +360,36 @@ enum nvme_vs { #define NVME_MINOR(ver) NVME_VS_MNR(ver) #define NVME_TERTIARY(ver) NVME_VS_TER(ver) +/** + * enum nvme_cc - This field indicates the controller configuration + * @NVME_CC_EN_SHIFT: Shift amount to get the enable + * @NVME_CC_CSS_SHIFT: Shift amount to get the I/O command set selected + * @NVME_CC_MPS_SHIFT: Shift amount to get the memory page size + * @NVME_CC_AMS_SHIFT: Shift amount to get the arbitration mechanism selected + * @NVME_CC_SHN_SHIFT: Shift amount to get the shutdown notification + * @NVME_CC_IOSQES_SHIFT: Shift amount to get the I/O submission queue entry size + * @NVME_CC_IOCQES_SHIFT: Shift amount to get the I/O completion queue entry size + * @NVME_CC_CRIME_SHIFT: Shift amount to get the controller ready independent of media enable + * @NVME_CC_EN_MASK: Mask to get the enable + * @NVME_CC_CSS_MASK: Mask to get the I/O command set selected + * @NVME_CC_MPS_MASK: Mask to get the memory page size + * @NVME_CC_AMS_MASK: Mask to get the arbitration mechanism selected + * @NVME_CC_SHN_MASK: Mask to get the shutdown notification + * @NVME_CC_CRIME_MASK: Mask to get the I/O submission queue entry size + * @NVME_CC_IOSQES_MASK: Mask to get the I/O completion queue entry size + * @NVME_CC_IOCQES_MASK: Mask to get the controller ready independent of media enable + * @NVME_CC_CSS_NVM: NVM command set + * @NVME_CC_CSS_CSI: All supported I/O command sets + * @NVME_CC_CSS_ADMIN: Admin command set only + * @NVME_CC_AMS_RR: Round robin + * @NVME_CC_AMS_WRRU: Weighted round robin with urgent priority class + * @NVME_CC_AMS_VS: Vendor specific + * @NVME_CC_SHN_NONE: No notification; no effect + * @NVME_CC_SHN_NORMAL: Normal shutdown notification + * @NVME_CC_SHN_ABRUPT: Abrupt shutdown notification + * @NVME_CC_CRWME: Controller ready with media enable + * @NVME_CC_CRIME: Controller ready independent of media enable + */ enum nvme_cc { NVME_CC_EN_SHIFT = 0, NVME_CC_CSS_SHIFT = 4, @@ -336,21 +429,42 @@ enum nvme_cc { #define NVME_CC_IOCQES(cc) NVME_GET(cc, CC_IOCQES) #define NVME_CC_CRIME(cc) NVME_GET(cc, CC_CRIME) +/** + * enum nvme_csts - This field indicates the controller status register + * @NVME_CSTS_RDY_SHIFT: Shift amount to get the ready + * @NVME_CSTS_CFS_SHIFT: Shift amount to get the controller fatal status + * @NVME_CSTS_SHST_SHIFT: Shift amount to get the shutdown status + * @NVME_CSTS_NSSRO_SHIFT: Shift amount to get the NVM subsystem reset occurred + * @NVME_CSTS_PP_SHIFT: Shift amount to get the processing paused + * @NVME_CSTS_ST_SHIFT: Shift amount to get the shutdown type + * @NVME_CSTS_RDY_MASK: Mask to get the ready + * @NVME_CSTS_CFS_MASK: Mask to get the controller fatal status + * @NVME_CSTS_SHST_MASK: Mask to get the shutdown status + * @NVME_CSTS_NSSRO_MASK: Mask to get the NVM subsystem reset occurred + * @NVME_CSTS_PP_MASK: Mask to get the processing paused + * @NVME_CSTS_ST_MASK: Mask to get the shutdown type + * @NVME_CSTS_SHST_NORMAL: Normal operation + * @NVME_CSTS_SHST_OCCUR: Shutdown processing occurring + * @NVME_CSTS_SHST_CMPLT: Shutdown processing complete + * @NVME_CSTS_SHN_MASK: Deprecated mask to get the shutdown status + */ enum nvme_csts { NVME_CSTS_RDY_SHIFT = 0, NVME_CSTS_CFS_SHIFT = 1, NVME_CSTS_SHST_SHIFT = 2, NVME_CSTS_NSSRO_SHIFT = 4, NVME_CSTS_PP_SHIFT = 5, + NVME_CSTS_ST_SHIFT = 6, NVME_CSTS_RDY_MASK = 0x1, NVME_CSTS_CFS_MASK = 0x1, - NVME_CSTS_SHN_MASK = 0x3, + NVME_CSTS_SHST_MASK = 0x3, NVME_CSTS_NSSRO_MASK = 0x1, NVME_CSTS_PP_MASK = 0x1, + NVME_CSTS_ST_MASK = 0x1, NVME_CSTS_SHST_NORMAL = 0, NVME_CSTS_SHST_OCCUR = 1, NVME_CSTS_SHST_CMPLT = 2, - NVME_CSTS_SHST_MASK = 3, + NVME_CSTS_SHN_MASK = NVME_CSTS_SHST_MASK, /* Deprecated */ }; #define NVME_CSTS_RDY(csts) NVME_GET(csts, CSTS_RDY) @@ -358,7 +472,15 @@ enum nvme_csts { #define NVME_CSTS_SHST(csts) NVME_GET(csts, CSTS_SHST) #define NVME_CSTS_NSSRO(csts) NVME_GET(csts, CSTS_NSSRO) #define NVME_CSTS_PP(csts) NVME_GET(csts, CSTS_PP) +#define NVME_CSTS_ST(csts) NVME_GET(csts, CSTS_ST) +/** + * enum nvme_aqa - This field indicates the admin queue attributes + * @NVME_AQA_ASQS_SHIFT: Shift amount to get the admin submission queue size + * @NVME_AQA_ACQS_SHIFT: Shift amount to get the admin completion queue size + * @NVME_AQA_ASQS_MASK: Mask to get the admin submission queue size + * @NVME_AQA_ACQS_MASK: Mask to get the admin completion queue size + */ enum nvme_aqa { NVME_AQA_ASQS_SHIFT = 0, NVME_AQA_ACQS_SHIFT = 16, @@ -369,6 +491,47 @@ enum nvme_aqa { #define NVME_AQA_ASQS(aqa) NVME_GET(aqa, AQA_ASQS) #define NVME_AQA_ACQS(aqa) NVME_GET(aqa, AQA_ACQS) +/** + * enum nvme_asq - This field indicates the admin submission queue base address + * @NVME_ASQ_ASQB_SHIFT: Shift amount to get the admin submission queue base + */ +enum nvme_asq { + NVME_ASQ_ASQB_SHIFT = 12, +}; +static const __u64 NVME_ASQ_ASQB_MASK = 0xfffffffffffffull; + +#define NVME_ASQ_ASQB(asq) NVME_GET(asq, ASQ_ASQB) + +/** + * enum nvme_acq - This field indicates the admin completion queue base address + * @NVME_ACQ_ACQB_SHIFT: Shift amount to get the admin completion queue base + */ +enum nvme_acq { + NVME_ACQ_ACQB_SHIFT = 12, +}; +static const __u64 NVME_ACQ_ACQB_MASK = 0xfffffffffffffull; + +#define NVME_ACQ_ACQB(acq) NVME_GET(acq, ACQ_ACQB) + +/** + * enum nvme_cmbloc - This field indicates the controller memory buffer location + * @NVME_CMBLOC_BIR_SHIFT: Shift amount to get the base indicator register + * @NVME_CMBLOC_CQMMS_SHIFT: Shift amount to get the CMB queue mixed memory support + * @NVME_CMBLOC_CQPDS_SHIFT: Shift amount to get the CMB queue physically discontiguous support + * @NVME_CMBLOC_CDPLMS_SHIFT: Shift amount to get the CMB data pointer mixed locations support + * @NVME_CMBLOC_CDPCILS_SHIFT: Shift amount to get the CMB data pointer and command independent locations support + * @NVME_CMBLOC_CDMMMS_SHIFT: Shift amount to get the CMB data metadata mixed memory support + * @NVME_CMBLOC_CQDA_SHIFT: Shift amount to get the CMB queue dword alignment + * @NVME_CMBLOC_OFST_SHIFT: Shift amount to get the offset + * @NVME_CMBLOC_BIR_MASK: Mask to get the base indicator register + * @NVME_CMBLOC_CQMMS_MASK: Mask to get the CMB queue mixed memory support + * @NVME_CMBLOC_CQPDS_MASK: Mask to get the CMB queue physically discontiguous support + * @NVME_CMBLOC_CDPLMS_MASK: Mask to get the CMB data pointer mixed locations support + * @NVME_CMBLOC_CDPCILS_MASK: Mask to get the CMB data pointer and command independent locations support + * @NVME_CMBLOC_CDMMMS_MASK: Mask to get the CMB data metadata mixed memory support + * @NVME_CMBLOC_CQDA_MASK: Mask to get the CMB queue dword alignment + * @NVME_CMBLOC_OFST_MASK: Mask to get the offset + */ enum nvme_cmbloc { NVME_CMBLOC_BIR_SHIFT = 0, NVME_CMBLOC_CQMMS_SHIFT = 3, @@ -397,6 +560,30 @@ enum nvme_cmbloc { #define NVME_CMBLOC_CQDA(cmbloc) NVME_GET(cmbloc, CMBLOC_CQDA) #define NVME_CMBLOC_OFST(cmbloc) NVME_GET(cmbloc, CMBLOC_OFST) +/** + * enum nvme_cmbsz - This field indicates the controller memory buffer size + * @NVME_CMBSZ_SQS_SHIFT: Shift amount to get the submission queue support + * @NVME_CMBSZ_CQS_SHIFT: Shift amount to get the completion queue support + * @NVME_CMBSZ_LISTS_SHIFT: Shift amount to get the PLP SGL list support + * @NVME_CMBSZ_RDS_SHIFT: Shift amount to get the read data support + * @NVME_CMBSZ_WDS_SHIFT: Shift amount to get the write data support + * @NVME_CMBSZ_SZU_SHIFT: Shift amount to get the size units + * @NVME_CMBSZ_SZ_SHIFT: Shift amount to get the size + * @NVME_CMBSZ_SQS_MASK: Mask to get the submission queue support + * @NVME_CMBSZ_CQS_MASK: Mask to get the completion queue support + * @NVME_CMBSZ_LISTS_MASK: Mask to get the PLP SGL list support + * @NVME_CMBSZ_RDS_MASK: Mask to get the read data support + * @NVME_CMBSZ_WDS_MASK: Mask to get the write data support + * @NVME_CMBSZ_SZU_MASK: Mask to get the size units + * @NVME_CMBSZ_SZ_MASK: Mask to get the size + * @NVME_CMBSZ_SZU_4K: 4 KiB + * @NVME_CMBSZ_SZU_64K: 64 KiB + * @NVME_CMBSZ_SZU_1M: 1 MiB + * @NVME_CMBSZ_SZU_16M: 16 MiB + * @NVME_CMBSZ_SZU_256M: 256 MiB + * @NVME_CMBSZ_SZU_4G: 4 GiB + * @NVME_CMBSZ_SZU_64G: 64 GiB + */ enum nvme_cmbsz { NVME_CMBSZ_SQS_SHIFT = 0, NVME_CMBSZ_CQS_SHIFT = 1, @@ -441,6 +628,19 @@ static inline __u64 nvme_cmb_size(__u32 cmbsz) (1ULL << (12 + 4 * NVME_CMBSZ_SZU(cmbsz))); } +/** + * enum nvme_bpinfo - This field indicates the boot partition information + * @NVME_BPINFO_BPSZ_SHIFT: Shift amount to get the boot partition size + * @NVME_BPINFO_BRS_SHIFT: Shift amount to get the boot read status + * @NVME_BPINFO_ABPID_SHIFT: Shift amount to get the active boot partition ID + * @NVME_BPINFO_BPSZ_MASK: Mask to get the boot partition size + * @NVME_BPINFO_BRS_MASK: Mask to get the boot read status + * @NVME_BPINFO_ABPID_MASK: Mask to get the active boot partition ID + * @NVME_BPINFO_BRS_NONE: No boot partition read operation requested + * @NVME_BPINFO_BRS_READ_IN_PROGRESS: Boot partition read in progress + * @NVME_BPINFO_BRS_READ_SUCCESS: Boot partition read completed successfully + * @NVME_BPINFO_BRS_READ_ERROR: Error completing boot partition read + */ enum nvme_bpinfo { NVME_BPINFO_BPSZ_SHIFT = 0, NVME_BPINFO_BRS_SHIFT = 24, @@ -458,12 +658,21 @@ enum nvme_bpinfo { #define NVME_BPINFO_BRS(bpinfo) NVME_GET(bpinfo, BPINFO_BRS) #define NVME_BPINFO_ABPID(bpinfo) NVME_GET(bpinfo, BPINFO_ABPID) +/** + * enum nvme_bprsel - This field indicates the boot partition read select + * @NVME_BPRSEL_BPRSZ_SHIFT: Shift amount to get the boot partition read size + * @NVME_BPRSEL_BPROF_SHIFT: Shift amount to get the boot partition read offset + * @NVME_BPRSEL_BPID_SHIFT: Shift amount to get the boot partition identifier + * @NVME_BPRSEL_BPRSZ_MASK: Mask to get the boot partition read size + * @NVME_BPRSEL_BPROF_MASK: Mask to get the boot partition read offset + * @NVME_BPRSEL_BPID_MASK: Mask to get the boot partition identifier + */ enum nvme_bprsel { NVME_BPRSEL_BPRSZ_SHIFT = 0, NVME_BPRSEL_BPROF_SHIFT = 10, NVME_BPRSEL_BPID_SHIFT = 31, NVME_BPRSEL_BPRSZ_MASK = 0x3ff, - NVME_BPRSEL_BPROF_MASK = 0xfff, + NVME_BPRSEL_BPROF_MASK = 0xfffff, NVME_BPRSEL_BPID_MASK = 0x1, }; @@ -471,6 +680,25 @@ enum nvme_bprsel { #define NVME_BPRSEL_BPROF(bprsel) NVME_GET(bprsel, BPRSEL_BPROF) #define NVME_BPRSEL_BPID(bprsel) NVME_GET(bprsel, BPRSEL_BPID) +/** + * enum nvme_bpmbl - This field indicates the boot partition memory buffer location + * @NVME_BPMBL_BMBBA_SHIFT: Shift amount to get the boot partition memory buffer base address + */ +enum nvme_bpmbl { + NVME_BPMBL_BMBBA_SHIFT = 12, +}; +static const __u64 NVME_BPMBL_BMBBA_MASK = 0xfffffffffffffull; + +#define NVME_BPMBL_BMBBA(bpmbl) NVME_GET(bpmbl, BPMBL_BMBBA) + +/** + * enum nvme_cmbmsc - This field indicates the controller memory buffer memory space control + * @NVME_CMBMSC_CRE_SHIFT: Shift amount to get the capabilities registers enabled + * @NVME_CMBMSC_CMSE_SHIFT: Shift amount to get the controller memory space enable + * @NVME_CMBMSC_CBA_SHIFT: Shift amount to get the controller base address + * @NVME_CMBMSC_CRE_MASK: Mask to get the capabilities registers enabled + * @NVME_CMBMSC_CMSE_MASK: Mask to get the controller memory space enable + */ enum nvme_cmbmsc { NVME_CMBMSC_CRE_SHIFT = 0, NVME_CMBMSC_CMSE_SHIFT = 1, @@ -484,6 +712,11 @@ static const __u64 NVME_CMBMSC_CBA_MASK = 0xfffffffffffffull; #define NVME_CMBMSC_CMSE(cmbmsc) NVME_GET(cmbmsc, CMBMSC_CMSE) #define NVME_CMBMSC_CBA(cmbmsc) NVME_GET(cmbmsc, CMBMSC_CBA) +/** + * enum nvme_cmbsts - This field indicates the controller memory buffer status + * @NVME_CMBSTS_CBAI_SHIFT: Shift amount to get the controller base address invalid + * @NVME_CMBSTS_CBAI_MASK: Mask to get the controller base address invalid + */ enum nvme_cmbsts { NVME_CMBSTS_CBAI_SHIFT = 0, NVME_CMBSTS_CBAI_MASK = 0x1, @@ -491,31 +724,130 @@ enum nvme_cmbsts { #define NVME_CMBSTS_CBAI(cmbsts) NVME_GET(cmbsts, CMBSTS_CBAI) +/** + * enum nvme_unit - Defined buffer size and write throughput granularity units + * @NVME_UNIT_B: Bytes or Bytes/second + * @NVME_UNIT_1K: 1 KiB or 1 KiB/second + * @NVME_UNIT_1M: 1 MiB or 1 MiB/second + * @NVME_UNIT_1G: 1 GiB or 1 GiB/second + */ +enum nvme_unit { + NVME_UNIT_B = 0, + NVME_UNIT_1K = 1, + NVME_UNIT_1M = 2, + NVME_UNIT_1G = 3, +}; + +/** + * enum nvme_cmbebs - This field indicates the controller memory buffer elasticity buffer size + * @NVME_CMBEBS_CMBSZU_SHIFT: Shift amount to get the CMB elasticity buffer size units + * @NVME_CMBEBS_RBB_SHIFT: Shift amount to get the read bypass behavior + * @NVME_CMBEBS_CMBWBZ_SHIFT: Shift amount to get the CMB elasiticity buffer size base + * @NVME_CMBEBS_CMBSZU_MASK: Mask to get the CMB elasticity buffer size units + * @NVME_CMBEBS_RBB_MASK: Mask to get the read bypass behavior + * @NVME_CMBEBS_CMBWBZ_MASK: Mask to get the CMB elasiticity buffer size base + * @NVME_CMBEBS_CMBSZU_B: Bytes granularity + * @NVME_CMBEBS_CMBSZU_1K: 1 KiB granularity + * @NVME_CMBEBS_CMBSZU_1M: 1 MiB granularity + * @NVME_CMBEBS_CMBSZU_1G: 1 GiB granularity + */ +enum nvme_cmbebs { + NVME_CMBEBS_CMBSZU_SHIFT = 0, + NVME_CMBEBS_RBB_SHIFT = 4, + NVME_CMBEBS_CMBWBZ_SHIFT = 8, + NVME_CMBEBS_CMBSZU_MASK = 0xf, + NVME_CMBEBS_RBB_MASK = 0x1, + NVME_CMBEBS_CMBWBZ_MASK = 0xffffff, + NVME_CMBEBS_CMBSZU_B = NVME_UNIT_B, + NVME_CMBEBS_CMBSZU_1K = NVME_UNIT_1K, + NVME_CMBEBS_CMBSZU_1M = NVME_UNIT_1M, + NVME_CMBEBS_CMBSZU_1G = NVME_UNIT_1G, +}; + +#define NVME_CMBEBS_CMBSZU(cmbebs) NVME_GET(cmbebs, CMBEBS_CMBSZU) +#define NVME_CMBEBS_RBB(cmbebs) NVME_GET(cmbebs, CMBEBS_RBB) +#define NVME_CMBEBS_CMBWBZ(cmbebs) NVME_GET(cmbebs, CMBEBS_CMBWBZ) + +/** + * enum nvme_cmbswtp - This field indicates the controller memory buffer sustained write throughput + * @NVME_CMBSWTP_CMBSWTU_SHIFT: Shift amount to get the CMB sustained write throughput units + * @NVME_CMBSWTP_CMBSWTV_SHIFT: Shift amount to get the CMB sustained write throughput + * @NVME_CMBSWTP_CMBSWTU_MASK: Mask to get the CMB sustained write throughput units + * @NVME_CMBSWTP_CMBSWTV_MASK: Mask to get the CMB sustained write throughput + * @NVME_CMBSWTP_CMBSWTU_B: Bytes/second granularity + * @NVME_CMBSWTP_CMBSWTU_1K: 1 KiB/second granularity + * @NVME_CMBSWTP_CMBSWTU_1M: 1 MiB/second granularity + * @NVME_CMBSWTP_CMBSWTU_1G: 1 GiB/second granularity + */ +enum nvme_cmbswtp { + NVME_CMBSWTP_CMBSWTU_SHIFT = 0, + NVME_CMBSWTP_CMBSWTV_SHIFT = 8, + NVME_CMBSWTP_CMBSWTU_MASK = 0xf, + NVME_CMBSWTP_CMBSWTV_MASK = 0xffffff, + NVME_CMBSWTP_CMBSWTU_B = NVME_UNIT_B, + NVME_CMBSWTP_CMBSWTU_1K = NVME_UNIT_1K, + NVME_CMBSWTP_CMBSWTU_1M = NVME_UNIT_1M, + NVME_CMBSWTP_CMBSWTU_1G = NVME_UNIT_1G, +}; + +#define NVME_CMBSWTP_CMBSWTU(cmbswtp) NVME_GET(cmbswtp, CMBSWTP_CMBSWTU) +#define NVME_CMBSWTP_CMBSWTV(cmbswtp) NVME_GET(cmbswtp, CMBSWTP_CMBSWTV) + +/** + * enum nvme_crto - This field indicates the controller ready timeouts + * @NVME_CRTO_CRWMT_SHIFT: Shift amount to get the controller ready with media timeout + * @NVME_CRTO_CRIMT_SHIFT: Shift amount to get the controller ready independent of media timeout + * @NVME_CRTO_CRWMT_MASK: Mask to get the controller ready with media timeout + * @NVME_CRTO_CRIMT_MASK: Mask to get the controller ready independent of media timeout + */ enum nvme_crto { - NVME_CRTO_CRIMT_SHIFT = 16, - NVME_CRTO_CRIMT_MASK = 0xffff0000, NVME_CRTO_CRWMT_SHIFT = 0, - NVME_CRTO_CRWMT_MASK = 0x0000ffff, + NVME_CRTO_CRIMT_SHIFT = 16, + NVME_CRTO_CRWMT_MASK = 0xffff, + NVME_CRTO_CRIMT_MASK = 0xffff, }; #define NVME_CRTO_CRIMT(crto) NVME_GET(crto, CRTO_CRIMT) #define NVME_CRTO_CRWMT(crto) NVME_GET(crto, CRTO_CRWMT) +/** + * enum nvme_pmrcap - This field indicates the persistent memory region capabilities + * @NVME_PMRCAP_RDS_SHIFT: Shift amount to get the read data support + * @NVME_PMRCAP_WDS_SHIFT: Shift amount to get the write data support + * @NVME_PMRCAP_BIR_SHIFT: Shift amount to get the base indicator register + * @NVME_PMRCAP_PMRTU_SHIFT: Shift amount to get the persistent memory region time units + * @NVME_PMRCAP_PMRWBM_SHIFT: Shift amount to get the persistent memory region write barrier mechanisms + * @NVME_PMRCAP_PMRTO_SHIFT: Shift amount to get the persistent memory region timeout + * @NVME_PMRCAP_CMSS_SHIFT: Shift amount to get the controller memory space supported + * @NVME_PMRCAP_PMRWMB_SHIFT: Deprecated shift amount to get the persistent memory region write barrier mechanisms + * @NVME_PMRCAP_RDS_MASK: Mask to get the read data support + * @NVME_PMRCAP_WDS_MASK: Mask to get the write data support + * @NVME_PMRCAP_BIR_MASK: Mask to get the base indicator register + * @NVME_PMRCAP_PMRTU_MASK: Mask to get the persistent memory region time units + * @NVME_PMRCAP_PMRWBM_MASK: Mask to get the persistent memory region write barrier mechanisms + * @NVME_PMRCAP_PMRTO_MASK: Mask to get the persistent memory region timeout + * @NVME_PMRCAP_CMSS_MASK: Mask to get the controller memory space supported + * @NVME_PMRCAP_PMRWMB_MASK: Deprecated mask to get the persistent memory region write barrier mechanisms + * @NVME_PMRCAP_PMRTU_500MS: 500 milliseconds + * @NVME_PMRCAP_PMRTU_60S: minutes + */ enum nvme_pmrcap { NVME_PMRCAP_RDS_SHIFT = 3, NVME_PMRCAP_WDS_SHIFT = 4, NVME_PMRCAP_BIR_SHIFT = 5, NVME_PMRCAP_PMRTU_SHIFT = 8, - NVME_PMRCAP_PMRWMB_SHIFT = 10, + NVME_PMRCAP_PMRWBM_SHIFT = 10, NVME_PMRCAP_PMRTO_SHIFT = 16, NVME_PMRCAP_CMSS_SHIFT = 24, + NVME_PMRCAP_PMRWMB_SHIFT = NVME_PMRCAP_PMRWBM_SHIFT, /* Deprecated */ NVME_PMRCAP_RDS_MASK = 0x1, NVME_PMRCAP_WDS_MASK = 0x1, NVME_PMRCAP_BIR_MASK = 0x7, NVME_PMRCAP_PMRTU_MASK = 0x3, - NVME_PMRCAP_PMRWMB_MASK = 0xf, + NVME_PMRCAP_PMRWBM_MASK = 0xf, NVME_PMRCAP_PMRTO_MASK = 0xff, NVME_PMRCAP_CMSS_MASK = 0x1, + NVME_PMRCAP_PMRWMB_MASK = NVME_PMRCAP_PMRWBM_MASK, /* Deprecated */ NVME_PMRCAP_PMRTU_500MS = 0, NVME_PMRCAP_PMRTU_60S = 1, }; @@ -524,10 +856,16 @@ enum nvme_pmrcap { #define NVME_PMRCAP_WDS(pmrcap) NVME_GET(pmrcap, PMRCAP_WDS) #define NVME_PMRCAP_BIR(pmrcap) NVME_GET(pmrcap, PMRCAP_BIR) #define NVME_PMRCAP_PMRTU(pmrcap) NVME_GET(pmrcap, PMRCAP_PMRTU) -#define NVME_PMRCAP_PMRWMB(pmrcap) NVME_GET(pmrcap, PMRCAP_PMRWMB) +#define NVME_PMRCAP_PMRWBM(pmrcap) NVME_GET(pmrcap, PMRCAP_PMRWBM) #define NVME_PMRCAP_PMRTO(pmrcap) NVME_GET(pmrcap, PMRCAP_PMRTO) #define NVME_PMRCAP_CMSS(pmrcap) NVME_GET(pmrcap, PMRCAP_CMSS) +#define NVME_PMRCAP_PMRWMB(pmrcap) NVME_GET(pmrcap, PMRCAP_PMRWMB) /* Deprecated */ +/** + * enum nvme_pmrctl - This field indicates the persistent memory region control + * @NVME_PMRCTL_EN_SHIFT: Shift amount to get the enable + * @NVME_PMRCTL_EN_MASK: Mask to get the enable + */ enum nvme_pmrctl { NVME_PMRCTL_EN_SHIFT = 0, NVME_PMRCTL_EN_MASK = 0x1, @@ -535,6 +873,17 @@ enum nvme_pmrctl { #define NVME_PMRCTL_EN(pmrctl) NVME_GET(pmrctl, PMRCTL_EN) +/** + * enum nvme_pmrsts - This field indicates the persistent memory region status + * @NVME_PMRSTS_ERR_SHIFT: Shift amount to get the error + * @NVME_PMRSTS_NRDY_SHIFT: Shift amount to get the not ready + * @NVME_PMRSTS_HSTS_SHIFT: Shift amount to get the health status + * @NVME_PMRSTS_CBAI_SHIFT: Shift amount to get the controller base address invalid + * @NVME_PMRSTS_ERR_MASK: Mask to get the error + * @NVME_PMRSTS_NRDY_MASK: Mask to get the not ready + * @NVME_PMRSTS_HSTS_MASK: Mask to get the health status + * @NVME_PMRSTS_CBAI_MASK: Mask to get the controller base address invalid + */ enum nvme_pmrsts { NVME_PMRSTS_ERR_SHIFT = 0, NVME_PMRSTS_NRDY_SHIFT = 8, @@ -551,6 +900,19 @@ enum nvme_pmrsts { #define NVME_PMRSTS_HSTS(pmrsts) NVME_GET(pmrsts, PMRSTS_HSTS) #define NVME_PMRSTS_CBAI(pmrsts) NVME_GET(pmrsts, PMRSTS_CBAI) +/** + * enum nvme_pmrebs - This field indicates the persistent memory region elasticity buffer size + * @NVME_PMREBS_PMRSZU_SHIFT: Shift amount to get the PMR elasticity buffer size units + * @NVME_PMREBS_RBB_SHIFT: Shift amount to get the read bypass behavior + * @NVME_PMREBS_PMRWBZ_SHIFT: Shift amount to get the PMR elasticity buffer size base + * @NVME_PMREBS_PMRSZU_MASK: Mask to get the PMR elasticity buffer size units + * @NVME_PMREBS_RBB_MASK: Mask to get the read bypass behavior + * @NVME_PMREBS_PMRWBZ_MASK: Mask to get the PMR elasticity buffer size base + * @NVME_PMREBS_PMRSZU_B: Bytes + * @NVME_PMREBS_PMRSZU_1K: 1 KiB + * @NVME_PMREBS_PMRSZU_1M: 1 MiB + * @NVME_PMREBS_PMRSZU_1G: 1 GiB + */ enum nvme_pmrebs { NVME_PMREBS_PMRSZU_SHIFT = 0, NVME_PMREBS_RBB_SHIFT = 4, @@ -558,10 +920,10 @@ enum nvme_pmrebs { NVME_PMREBS_PMRSZU_MASK = 0xf, NVME_PMREBS_RBB_MASK = 0x1, NVME_PMREBS_PMRWBZ_MASK = 0xffffff, - NVME_PMREBS_PMRSZU_B = 0, - NVME_PMREBS_PMRSZU_1K = 1, - NVME_PMREBS_PMRSZU_1M = 2, - NVME_PMREBS_PMRSZU_1G = 3, + NVME_PMREBS_PMRSZU_B = NVME_UNIT_B, + NVME_PMREBS_PMRSZU_1K = NVME_UNIT_1K, + NVME_PMREBS_PMRSZU_1M = NVME_UNIT_1M, + NVME_PMREBS_PMRSZU_1G = NVME_UNIT_1G, }; #define NVME_PMREBS_PMRSZU(pmrebs) NVME_GET(pmrebs, PMREBS_PMRSZU) @@ -581,15 +943,26 @@ static inline __u64 nvme_pmr_size(__u32 pmrebs) (1ULL << (10 * NVME_PMREBS_PMRSZU(pmrebs))); } +/** + * enum nvme_pmrswtp - This field indicates the persistent memory region sustained write throughput + * @NVME_PMRSWTP_PMRSWTU_SHIFT: Shift amount to get the PMR sustained write throughput units + * @NVME_PMRSWTP_PMRSWTV_SHIFT: Shift amount to get the PMR sustained write throughput + * @NVME_PMRSWTP_PMRSWTU_MASK: Mask to get the PMR sustained write throughput units + * @NVME_PMRSWTP_PMRSWTV_MASK: Mask to get the PMR sustained write throughput + * @NVME_PMRSWTP_PMRSWTU_BPS: Bytes per second + * @NVME_PMRSWTP_PMRSWTU_KBPS: 1 KiB / s + * @NVME_PMRSWTP_PMRSWTU_MBPS: 1 MiB / s + * @NVME_PMRSWTP_PMRSWTU_GBPS: 1 GiB / s + */ enum nvme_pmrswtp { NVME_PMRSWTP_PMRSWTU_SHIFT = 0, NVME_PMRSWTP_PMRSWTV_SHIFT = 8, NVME_PMRSWTP_PMRSWTU_MASK = 0xf, NVME_PMRSWTP_PMRSWTV_MASK = 0xffffff, - NVME_PMRSWTP_PMRSWTU_BPS = 0, - NVME_PMRSWTP_PMRSWTU_KBPS = 1, - NVME_PMRSWTP_PMRSWTU_MBPS = 2, - NVME_PMRSWTP_PMRSWTU_GBPS = 3, + NVME_PMRSWTP_PMRSWTU_BPS = NVME_UNIT_B, + NVME_PMRSWTP_PMRSWTU_KBPS = NVME_UNIT_1K, + NVME_PMRSWTP_PMRSWTU_MBPS = NVME_UNIT_1M, + NVME_PMRSWTP_PMRSWTU_GBPS = NVME_UNIT_1G, }; #define NVME_PMRSWTP_PMRSWTU(pmrswtp) NVME_GET(pmrswtp, PMRSWTP_PMRSWTU) @@ -607,6 +980,12 @@ static inline __u64 nvme_pmr_throughput(__u32 pmrswtp) (1ULL << (10 * NVME_PMRSWTP_PMRSWTU(pmrswtp))); } +/** + * enum nvme_pmrmsc - This field indicates the persistent memory region memory space control + * @NVME_PMRMSC_CMSE_SHIFT: Shift amount to get the controller memory space enable + * @NVME_PMRMSC_CBA_SHIFT: Shift amount to get the controller base address + * @NVME_PMRMSC_CMSE_MASK: Mask to get the controller memory space enable + */ enum nvme_pmrmsc { NVME_PMRMSC_CMSE_SHIFT = 1, NVME_PMRMSC_CBA_SHIFT = 12, @@ -617,6 +996,15 @@ static const __u64 NVME_PMRMSC_CBA_MASK = 0xfffffffffffffull; #define NVME_PMRMSC_CMSE(pmrmsc) NVME_GET(pmrmsc, PMRMSC_CMSE) #define NVME_PMRMSC_CBA(pmrmsc) NVME_GET(pmrmsc, PMRMSC_CBA) +/** + * enum nvme_flbas - This field indicates the formatted LBA size + * @NVME_FLBAS_LOWER_SHIFT: Shift amount to get the format index least significant 4 bits + * @NVME_FLBAS_META_EXT_SHIFT: Shift amount to get the metadata transferred + * @NVME_FLBAS_HIGHER_SHIFT: Shift amount to get the format index most significant 2 bits + * @NVME_FLBAS_LOWER_MASK: Mask to get the format index least significant 4 bits + * @NVME_FLBAS_META_EXT_MASK: Mask to get the metadata transferred + * @NVME_FLBAS_HIGHER_MASK: Mask to get the format index most significant 2 bits + */ enum nvme_flbas { NVME_FLBAS_LOWER_SHIFT = 0, NVME_FLBAS_META_EXT_SHIFT = 4, @@ -1077,6 +1465,38 @@ struct nvme_id_ctrl { }; /** + * enum nvme_cmic - This field indicates the controller multi-path I/O and NS sharing capabilities + * @NVME_CMIC_MULTI_PORT_SHIFT: Shift amount to get the NVM subsystem port + * @NVME_CMIC_MULTI_CTRL_SHIFT: Shift amount to get the controllers + * @NVME_CMIC_MULTI_SRIOV_SHIFT: Shift amount to get the SR-IOV virtual function + * @NVME_CMIC_MULTI_ANA_SHIFT: Shift amount to get the asymmetric namespace access reporting + * @NVME_CMIC_MULTI_RSVD_SHIFT: Shift amount to get the reserved + * @NVME_CMIC_MULTI_PORT_MASK: Mask to get the NVM subsystem port + * @NVME_CMIC_MULTI_CTRL_MASK: Mask to get the controllers + * @NVME_CMIC_MULTI_SRIOV_MASK: Mask to get the SR-IOV virtual function + * @NVME_CMIC_MULTI_ANA_MASK: Mask to get the asymmetric namespace access reporting + * @NVME_CMIC_MULTI_RSVD_MASK: Mask to get the reserved + */ +enum nvme_cmic { + NVME_CMIC_MULTI_PORT_SHIFT = 0, + NVME_CMIC_MULTI_CTRL_SHIFT = 1, + NVME_CMIC_MULTI_SRIOV_SHIFT = 2, + NVME_CMIC_MULTI_ANA_SHIFT = 3, + NVME_CMIC_MULTI_RSVD_SHIFT = 4, + NVME_CMIC_MULTI_PORT_MASK = 0x1, + NVME_CMIC_MULTI_CTRL_MASK = 0x1, + NVME_CMIC_MULTI_SRIOV_MASK = 0x1, + NVME_CMIC_MULTI_ANA_MASK = 0x1, + NVME_CMIC_MULTI_RSVD_MASK = 0xf, +}; + +#define NVME_CMIC_MULTI_PORT(cmic) NVME_GET(cmic, CMIC_MULTI_PORT) +#define NVME_CMIC_MULTI_CTRL(cmic) NVME_GET(cmic, CMIC_MULTI_CTRL) +#define NVME_CMIC_MULTI_SRIOV(cmic) NVME_GET(cmic, CMIC_MULTI_SRIOV) +#define NVME_CMIC_MULTI_ANA(cmic) NVME_GET(cmic, CMIC_MULTI_ANA) +#define NVME_CMIC_MULTI_RSVD(cmic) NVME_GET(cmic, CMIC_MULTI_RSVD) + +/** * enum nvme_id_ctrl_cmic - Controller Multipath IO and Namespace Sharing * Capabilities of the controller and NVM subsystem. * @NVME_CTRL_CMIC_MULTI_PORT: If set, then the NVM subsystem may contain @@ -3340,6 +3760,53 @@ struct nvme_persistent_event_log { } __attribute__((packed)); /** + * enum nvme_pel_rci - This field indicates the persistent event log reporting context + * @NVME_PEL_RCI_RCPID_SHIFT: Shift amount to get the reporting context port identifier + * from the &struct nvme_persistent_event_log.rci field. + * @NVME_PEL_RCI_RCPIT_SHIFT: Shift amount to get the reporting context port identifier + * type from the &struct nvme_persistent_event_log.rci field. + * @NVME_PEL_RCI_RCE_SHIFT: Shift amount to get the reporting context exists + * from the &struct nvme_persistent_event_log.rci field. + * @NVME_PEL_RCI_RSVD_SHIFT: Shift amount to get the reserved reporting context + * from the &struct nvme_persistent_event_log.rci field. + * @NVME_PEL_RCI_RCPID_MASK: Mask to get the reporting context port identifier from + * the &struct nvme_persistent_event_log.rci field. + * @NVME_PEL_RCI_RCPIT_MASK: Mask to get the reporting context port identifier type from + * the &struct nvme_persistent_event_log.rci field. + * @NVME_PEL_RCI_RCE_MASK: Mask to get the reporting context exists from + * the &struct nvme_persistent_event_log.rci field. + * @NVME_PEL_RCI_RSVD_MASK: Mask to get the reserved reporting context from + * the &struct nvme_persistent_event_log.rci field. + */ +enum nvme_pel_rci { + NVME_PEL_RCI_RCPID_SHIFT = 0, + NVME_PEL_RCI_RCPIT_SHIFT = 16, + NVME_PEL_RCI_RCE_SHIFT = 18, + NVME_PEL_RCI_RSVD_SHIFT = 19, + NVME_PEL_RCI_RCPID_MASK = 0xffff, + NVME_PEL_RCI_RCPIT_MASK = 0x3, + NVME_PEL_RCI_RCE_MASK = 0x1, + NVME_PEL_RCI_RSVD_MASK = 0x1fff, +}; + +#define NVME_PEL_RCI_RCPID(rci) NVME_GET(rci, PEL_RCI_RCPID) +#define NVME_PEL_RCI_RCPIT(rci) NVME_GET(rci, PEL_RCI_RCPIT) +#define NVME_PEL_RCI_RCE(rci) NVME_GET(rci, PEL_RCI_RCE) +#define NVME_PEL_RCI_RSVD(rci) NVME_GET(rci, PEL_RCI_RSVD) + +/** + * enum nvme_pel_rci_rcpit - Persistent Event Log Reporting Context - Port Identifier Type + * @NVME_PEL_RCI_RCPIT_NOT_EXIST: Does not already exist + * @NVME_PEL_RCI_RCPIT_EST_PORT: Established by an NVM subsystem port + * @NVME_PEL_RCI_RCPIT_EST_ME: Established by a Management Endpoint + */ +enum nvme_pel_rci_rcpit { + NVME_PEL_RCI_RCPIT_NOT_EXIST = 0, + NVME_PEL_RCI_RCPIT_EST_PORT = 1, + NVME_PEL_RCI_RCPIT_EST_ME = 2, +}; + +/** * struct nvme_persistent_event_entry - Persistent Event * @etype: Event Type * @etype_rev: Event Type Revision @@ -3398,6 +3865,41 @@ enum nvme_persistent_event_types { }; /** + * enum nvme_pel_ehai - This field indicates the persistent event header additional information + * @NVME_PEL_EHAI_PIT_SHIFT: Shift amount to get the reporting context port identifier + * from the &struct nvme_persistent_event_log.rci field. + * @NVME_PEL_EHAI_RSVD_SHIFT: Shift amount to get the reserved reporting context + * from the &struct nvme_persistent_event_log.rci field. + * @NVME_PEL_EHAI_PIT_MASK: Mask to get the reporting context port identifier from + * the &struct nvme_st_result.dsts field. + * @NVME_PEL_EHAI_RSVD_MASK: Mask to get the reserved reporting context from + * the &struct nvme_st_result.dsts field. + */ +enum nvme_pel_ehai { + NVME_PEL_EHAI_PIT_SHIFT = 0, + NVME_PEL_EHAI_RSVD_SHIFT = 2, + NVME_PEL_EHAI_PIT_MASK = 0x3, + NVME_PEL_EHAI_RSVD_MASK = 0x3f, +}; + +#define NVME_PEL_EHAI_PIT(ehai) NVME_GET(ehai, PEL_EHAI_PIT) +#define NVME_PEL_EHAI_RSVD(ehai) NVME_GET(ehai, PEL_EHAI_RSVD) + +/** + * enum nvme_pel_ehai_pit - Persistent Event Header Additional Information - Port Identifier Type + * @NVME_PEL_EHAI_PIT_NOT_REPORTED: PIT not reported and PELPID does not apply + * @NVME_PEL_EHAI_PIT_NSS_PORT: NVM subsystem port + * @NVME_PEL_EHAI_PIT_NMI_PORT: NVMe-MI port + * @NVME_PEL_EHAI_PIT_NOT_ASSOCIATED: Event not associated with any port and PELPID does not apply + */ +enum nvme_pel_ehai_pit { + NVME_PEL_EHAI_PIT_NOT_REPORTED = 0, + NVME_PEL_EHAI_PIT_NSS_PORT = 1, + NVME_PEL_EHAI_PIT_NMI_PORT = 2, + NVME_PEL_EHAI_PIT_NOT_ASSOCIATED = 3, +}; + +/** * struct nvme_fw_commit_event - Firmware Commit Event Data * @old_fw_rev: Old Firmware Revision * @new_fw_rev: New Firmware Revision @@ -3570,6 +4072,34 @@ struct nvme_set_feature_event { }; /** + * enum nvme_set_feat_event_layout - This field indicates the set feature event layout + * @NVME_SET_FEAT_EVENT_DW_COUNT_SHIFT: Shift amount to get the Dword count from the + * &struct nvme_set_feature_event.layout field. + * @NVME_SET_FEAT_EVENT_CC_DW0_SHIFT: Shift amount to get the logged command completion Dword 0 + * from the &struct nvme_set_feature_event.layout field. + * @NVME_SET_FEAT_EVENT_MB_COUNT_SHIFT: Shift amount to get the memory buffer count from + * the &struct nvme_set_feature_event.layout field. + * @NVME_SET_FEAT_EVENT_DW_COUNT_MASK: Mask to get the Dword count from the &struct + * nvme_set_feature_event.layout field. + * @NVME_SET_FEAT_EVENT_CC_DW0_MASK: Mask to get the logged command completion Dword 0 from + * the &struct nvme_set_feature_event.layout field. + * @NVME_SET_FEAT_EVENT_MB_COUNT_MASK: Mask to get the memory buffer count from the &struct + * nvme_set_feature_event.layout field. + */ +enum nvme_set_feat_event_layout { + NVME_SET_FEAT_EVENT_DW_COUNT_SHIFT = 0, + NVME_SET_FEAT_EVENT_CC_DW0_SHIFT = 3, + NVME_SET_FEAT_EVENT_MB_COUNT_SHIFT = 16, + NVME_SET_FEAT_EVENT_DW_COUNT_MASK = 0x7, + NVME_SET_FEAT_EVENT_CC_DW0_MASK = 0x1, + NVME_SET_FEAT_EVENT_MB_COUNT_MASK = 0xffff, +}; + +#define NVME_SET_FEAT_EVENT_DW_COUNT(layout) NVME_GET(layout, SET_FEAT_EVENT_DW_COUNT) +#define NVME_SET_FEAT_EVENT_CC_DW0(layout) NVME_GET(layout, SET_FEAT_EVENT_CC_DW0) +#define NVME_SET_FEAT_EVENT_MB_COUNT(layout) NVME_GET(layout, SET_FEAT_EVENT_MB_COUNT) + +/** * struct nvme_thermal_exc_event - Thermal Excursion Event Data * @over_temp: Over Temperature * @threshold: temperature threshold @@ -3749,6 +4279,27 @@ struct nvme_boot_partition { }; /** + * enum nvme_boot_partition_info - This field indicates the boot partition information + * @NVME_BOOT_PARTITION_INFO_BPSZ_SHIFT: Shift amount to get the boot partition size from + * the &struct nvme_boot_partition.bpinfo field. + * @NVME_BOOT_PARTITION_INFO_ABPID_SHIFT: Shift amount to get the active boot partition ID + * from the &struct nvme_boot_partition.bpinfo field. + * @NVME_BOOT_PARTITION_INFO_BPSZ_MASK: Mask to get the boot partition size from the + * &struct nvme_boot_partition.bpinfo field. + * @NVME_BOOT_PARTITION_INFO_ABPID_MASK: Mask to get the active boot partition ID from the + * &struct nvme_boot_partition.bpinfo field. + */ +enum nvme_boot_partition_info { + NVME_BOOT_PARTITION_INFO_BPSZ_SHIFT = 0, + NVME_BOOT_PARTITION_INFO_ABPID_SHIFT = 31, + NVME_BOOT_PARTITION_INFO_BPSZ_MASK = 0x7fff, + NVME_BOOT_PARTITION_INFO_ABPID_MASK = 0x1, +}; + +#define NVME_BOOT_PARTITION_INFO_BPSZ(bpinfo) NVME_GET(bpinfo, BOOT_PARTITION_INFO_BPSZ) +#define NVME_BOOT_PARTITION_INFO_ABPID(bpinfo) NVME_GET(bpinfo, BOOT_PARTITION_INFO_ABPID) + +/** * struct nvme_eom_lane_desc - EOM Lane Descriptor * @rsvd0: Reserved * @mstatus: Measurement Status @@ -3831,13 +4382,41 @@ struct nvme_phy_rx_eom_log { }; /** - * enum nvme_eom_optional_data - EOM Optional Data Present Fields - * @NVME_EOM_EYE_DATA_PRESENT: Eye Data Present + * enum nvme_eom_optional_data_present - EOM Optional Data Present Fields + * @NVME_EOM_ODP_PEFP_SHIFT: Shift amount to get the printable eye field present + * from the &struct nvme_phy_rx_eom_log.odp field. + * @NVME_EOM_ODP_EDFP_SHIFT: Shift amount to get the eye data field present + * from the &struct nvme_phy_rx_eom_log.odp field. + * @NVME_EOM_ODP_RSVD_SHIFT: Shift amount to get the reserved optional data present + * from the &struct nvme_phy_rx_eom_log.odp field. + * @NVME_EOM_ODP_PEFP_MASK: Mask to get the printable eye field present + * from the &struct nvme_phy_rx_eom_log.odp field. + * @NVME_EOM_ODP_EDFP_MASK: Mask to get the eye data field present + * from the &struct nvme_phy_rx_eom_log.odp field. + * @NVME_EOM_ODP_RSVD_MASK: Mask to get the reserved data present + * from the &struct nvme_phy_rx_eom_log.odp field. + */ +enum nvme_eom_optional_data_present { + NVME_EOM_ODP_PEFP_SHIFT = 0, + NVME_EOM_ODP_EDFP_SHIFT = 1, + NVME_EOM_ODP_RSVD_SHIFT = 2, + NVME_EOM_ODP_PEFP_MASK = 0x1, + NVME_EOM_ODP_EDFP_MASK = 0x1, + NVME_EOM_ODP_RSVD_MASK = 0x3f, +}; + +#define NVME_EOM_ODP_PEFP(odp) NVME_GET(odp, EOM_ODP_PEFP) +#define NVME_EOM_ODP_EDFP(odp) NVME_GET(odp, EOM_ODP_EDFP) +#define NVME_EOM_ODP_RSVD(odp) NVME_GET(odp, EOM_ODP_RSVD) + +/** + * enum nvme_eom_optional_data - EOM Optional Data Present Fields (Deprecated) * @NVME_EOM_PRINTABLE_EYE_PRESENT: Printable Eye Present + * @NVME_EOM_EYE_DATA_PRESENT: Eye Data Present */ enum nvme_eom_optional_data { - NVME_EOM_EYE_DATA_PRESENT = 1, - NVME_EOM_PRINTABLE_EYE_PRESENT = 1 << 1, + NVME_EOM_PRINTABLE_EYE_PRESENT = NVME_EOM_ODP_PEFP_MASK << NVME_EOM_ODP_PEFP_SHIFT, + NVME_EOM_EYE_DATA_PRESENT = NVME_EOM_ODP_EDFP_MASK << NVME_EOM_ODP_EDFP_SHIFT, }; /** @@ -6917,6 +7496,7 @@ enum nvme_identify_cns { * @NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST: Supported Capacity Configuration Lis * @NVME_LOG_LID_FID_SUPPORTED_EFFECTS: Feature Identifiers Supported and Effects * @NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS: NVMe-MI Commands Supported and Effects + * @NVME_LOG_LID_CMD_AND_FEAT_LOCKDOWN: Command and Feature Lockdown * @NVME_LOG_LID_BOOT_PARTITION: Boot Partition * @NVME_LOG_LID_PHY_RX_EOM: Physical Interface Receiver Eye Opening Measurement * @NVME_LOG_LID_FDP_CONFIGS: FDP Configurations @@ -6949,6 +7529,7 @@ enum nvme_cmd_get_log_lid { NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST = 0x11, NVME_LOG_LID_FID_SUPPORTED_EFFECTS = 0x12, NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS = 0x13, + NVME_LOG_LID_CMD_AND_FEAT_LOCKDOWN = 0x14, NVME_LOG_LID_BOOT_PARTITION = 0x15, NVME_LOG_LID_PHY_RX_EOM = 0x19, NVME_LOG_LID_FDP_CONFIGS = 0x20, diff --git a/src/nvme/util.c b/src/nvme/util.c index f091da7..7404509 100644 --- a/src/nvme/util.c +++ b/src/nvme/util.c @@ -12,6 +12,7 @@ #include <stdbool.h> #include <string.h> #include <errno.h> +#include <malloc.h> #include <sys/stat.h> #include <fcntl.h> @@ -619,6 +620,7 @@ static const char * const libnvme_status[] = { [ENVME_CONNECT_OPNOTSUPP] = "not supported", [ENVME_CONNECT_CONNREFUSED] = "connection refused", [ENVME_CONNECT_ADDRNOTAVAIL] = "cannot assign requested address", + [ENVME_CONNECT_IGNORED] = "connection ignored", }; const char *nvme_errno_to_string(int status) @@ -1135,3 +1137,17 @@ void *__nvme_alloc(size_t len) memset(p, 0, _len); return p; } + +void *__nvme_realloc(void *p, size_t len) +{ + size_t old_len = malloc_usable_size(p); + + void *result = __nvme_alloc(len); + + if (p) { + memcpy(result, p, min(old_len, len)); + free(p); + } + + return result; +} diff --git a/src/nvme/util.h b/src/nvme/util.h index 517c696..71fea9f 100644 --- a/src/nvme/util.h +++ b/src/nvme/util.h @@ -483,7 +483,7 @@ static inline void nvme_feature_decode_namespace_write_protect(__u32 value, static inline void nvme_id_ns_flbas_to_lbaf_inuse(__u8 flbas, __u8 *lbaf_inuse) { - *lbaf_inuse = ((NVME_FLBAS_HIGHER(flbas) >> 1) | + *lbaf_inuse = ((NVME_FLBAS_HIGHER(flbas) << 4) | NVME_FLBAS_LOWER(flbas)); } @@ -558,6 +558,8 @@ char *kv_keymatch(const char *kv, const char *key); */ char *startswith(const char *s, const char *prefix); +#define min(x, y) ((x) > (y) ? (y) : (x)) + #define __round_mask(val, mult) ((__typeof__(val))((mult)-1)) /** |