xref: /illumos-gate/usr/src/common/nvme/nvme_field.c (revision 533affcb)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2024 Oxide Computer Company
14  */
15 
16 /*
17  * This file contains shared pieces of the NVMe field validation logic and has
18  * shared pieces that are used between different parts.
19  */
20 
21 #include "nvme_common.h"
22 
23 #include <sys/sysmacros.h>
24 #ifdef	_KERNEL
25 #include <sys/sunddi.h>
26 #include <sys/stdint.h>
27 #else
28 #include <stdio.h>
29 #include <inttypes.h>
30 #endif
31 
32 bool
nvme_field_atleast(const nvme_valid_ctrl_data_t * data,const nvme_version_t * targ)33 nvme_field_atleast(const nvme_valid_ctrl_data_t *data,
34     const nvme_version_t *targ)
35 {
36 	return (nvme_vers_atleast(data->vcd_vers, targ));
37 }
38 
39 bool
nvme_field_supported_nsid(const nvme_field_info_t * field,const nvme_valid_ctrl_data_t * data,char * msg,size_t msglen)40 nvme_field_supported_nsid(const nvme_field_info_t *field,
41     const nvme_valid_ctrl_data_t *data, char *msg, size_t msglen)
42 {
43 	if (data->vcd_id->id_oacs.oa_nsmgmt != 0) {
44 		return (true);
45 	}
46 
47 	(void) snprintf(msg, msglen, "controller does not support field %s "
48 	    "(%s): missing namespace support in Optional Admin Command Support "
49 	    "(OACS)", field->nlfi_human, field->nlfi_spec);
50 	return (false);
51 }
52 
53 /*
54  * Note, we rely on external logic to determine if the broadcast nsid is valid.
55  * We always accept it.
56  */
57 bool
nvme_field_valid_nsid(const nvme_field_info_t * field,const nvme_valid_ctrl_data_t * data,uint64_t nsid,char * msg,size_t msglen)58 nvme_field_valid_nsid(const nvme_field_info_t *field,
59     const nvme_valid_ctrl_data_t *data, uint64_t nsid, char *msg, size_t msglen)
60 {
61 	if ((nsid != 0 && nsid <= data->vcd_id->id_nn) ||
62 	    nsid == NVME_NSID_BCAST) {
63 		return (true);
64 	}
65 
66 	(void) snprintf(msg, msglen, "namespace id %" PRIu64 "is outside the "
67 	    "valid range [0x%x, 0x%x], the broadcast nsid (0x%x) may be valid",
68 	    nsid, NVME_NSID_MIN, NVME_NSID_BCAST, data->vcd_id->id_nn);
69 	return (false);
70 }
71 
72 bool
nvme_field_range_check(const nvme_field_info_t * field,uint64_t min,uint64_t max,char * msg,size_t msglen,uint64_t value)73 nvme_field_range_check(const nvme_field_info_t *field, uint64_t min,
74     uint64_t max, char *msg, size_t msglen, uint64_t value)
75 {
76 	if (value >= min && value <= max) {
77 		return (true);
78 	}
79 
80 	(void) snprintf(msg, msglen, "field %s (%s) value 0x%"
81 	    PRIx64 " is outside the valid range: [0x%" PRIx64 ", 0x%" PRIx64
82 	    "]", field->nlfi_human, field->nlfi_spec, value, min, max);
83 	return (false);
84 }
85 
86 /*
87  * This is a general validation function for fields that are part of a command.
88  * It will check if the field is supported by the controller and if so, that its
89  * value is within the expected range. On error, an optional message will be
90  * written that explains the error. This is intended to be shared between
91  * userland and the kernel. The kernel should pass NULL/0 for msg/msglen because
92  * there is no message translation capability in the kernel.
93  */
94 nvme_field_error_t
nvme_field_validate(const nvme_field_info_t * field,const nvme_valid_ctrl_data_t * data,uint64_t value,char * msg,size_t msglen)95 nvme_field_validate(const nvme_field_info_t *field,
96     const nvme_valid_ctrl_data_t *data, uint64_t value, char *msg,
97     size_t msglen)
98 {
99 	ASSERT3P(field->nlfi_vers, !=, NULL);
100 
101 	if (!nvme_field_atleast(data, field->nlfi_vers)) {
102 		(void) snprintf(msg, msglen, "field %s (%s) requires "
103 		    "version %u.%u, but device is at %u.%u", field->nlfi_human,
104 		    field->nlfi_spec, data->vcd_vers->v_major,
105 		    data->vcd_vers->v_minor, field->nlfi_vers->v_major,
106 		    field->nlfi_vers->v_minor);
107 		return (NVME_FIELD_ERR_UNSUP_VERSION);
108 	}
109 
110 	if (field->nlfi_sup != NULL && !field->nlfi_sup(field, data, msg,
111 	    msglen)) {
112 		(void) snprintf(msg, msglen, "field %s (%s) is not "
113 		    "supported by the controller", field->nlfi_human,
114 		    field->nlfi_spec);
115 		return (NVME_FIELD_ERR_UNSUP_FIELD);
116 	}
117 
118 	if (field->nlfi_valid != NULL) {
119 		if (!field->nlfi_valid(field, data, value, msg, msglen)) {
120 			(void) snprintf(msg, msglen, "field %s (%s) "
121 			    "value 0x%" PRIx64 " is invalid", field->nlfi_human,
122 			    field->nlfi_spec, value);
123 			return (NVME_FIELD_ERR_BAD_VALUE);
124 		}
125 	} else if (!nvme_field_range_check(field, 0, field->nlfi_max_size, msg,
126 	    msglen, value)) {
127 		return (NVME_FIELD_ERR_BAD_VALUE);
128 	}
129 
130 	return (NVME_FIELD_ERR_OK);
131 }
132