1533affcbSRobert Mustacchi /*
2533affcbSRobert Mustacchi * This file and its contents are supplied under the terms of the
3533affcbSRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4533affcbSRobert Mustacchi * You may only use this file in accordance with the terms of version
5533affcbSRobert Mustacchi * 1.0 of the CDDL.
6533affcbSRobert Mustacchi *
7533affcbSRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8533affcbSRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9533affcbSRobert Mustacchi * http://www.illumos.org/license/CDDL.
10533affcbSRobert Mustacchi */
11533affcbSRobert Mustacchi
12533affcbSRobert Mustacchi /*
13533affcbSRobert Mustacchi * Copyright 2024 Oxide Computer Company
14533affcbSRobert Mustacchi */
15533affcbSRobert Mustacchi
16533affcbSRobert Mustacchi /*
17533affcbSRobert Mustacchi * This file contains shared pieces of the NVMe field validation logic and has
18533affcbSRobert Mustacchi * shared pieces that are used between different parts.
19533affcbSRobert Mustacchi */
20533affcbSRobert Mustacchi
21533affcbSRobert Mustacchi #include "nvme_common.h"
22533affcbSRobert Mustacchi
23533affcbSRobert Mustacchi #include <sys/sysmacros.h>
24533affcbSRobert Mustacchi #ifdef _KERNEL
25533affcbSRobert Mustacchi #include <sys/sunddi.h>
26533affcbSRobert Mustacchi #include <sys/stdint.h>
27533affcbSRobert Mustacchi #else
28533affcbSRobert Mustacchi #include <stdio.h>
29533affcbSRobert Mustacchi #include <inttypes.h>
30533affcbSRobert Mustacchi #endif
31533affcbSRobert Mustacchi
32533affcbSRobert Mustacchi bool
nvme_field_atleast(const nvme_valid_ctrl_data_t * data,const nvme_version_t * targ)33533affcbSRobert Mustacchi nvme_field_atleast(const nvme_valid_ctrl_data_t *data,
34533affcbSRobert Mustacchi const nvme_version_t *targ)
35533affcbSRobert Mustacchi {
36533affcbSRobert Mustacchi return (nvme_vers_atleast(data->vcd_vers, targ));
37533affcbSRobert Mustacchi }
38533affcbSRobert Mustacchi
39533affcbSRobert Mustacchi /*
40533affcbSRobert Mustacchi * Note, we rely on external logic to determine if the broadcast nsid is valid.
41533affcbSRobert Mustacchi * We always accept it.
42533affcbSRobert Mustacchi */
43533affcbSRobert Mustacchi 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)44533affcbSRobert Mustacchi nvme_field_valid_nsid(const nvme_field_info_t *field,
45533affcbSRobert Mustacchi const nvme_valid_ctrl_data_t *data, uint64_t nsid, char *msg, size_t msglen)
46533affcbSRobert Mustacchi {
47533affcbSRobert Mustacchi if ((nsid != 0 && nsid <= data->vcd_id->id_nn) ||
48533affcbSRobert Mustacchi nsid == NVME_NSID_BCAST) {
49533affcbSRobert Mustacchi return (true);
50533affcbSRobert Mustacchi }
51533affcbSRobert Mustacchi
52533affcbSRobert Mustacchi (void) snprintf(msg, msglen, "namespace id %" PRIu64 "is outside the "
53533affcbSRobert Mustacchi "valid range [0x%x, 0x%x], the broadcast nsid (0x%x) may be valid",
54533affcbSRobert Mustacchi nsid, NVME_NSID_MIN, NVME_NSID_BCAST, data->vcd_id->id_nn);
55533affcbSRobert Mustacchi return (false);
56533affcbSRobert Mustacchi }
57533affcbSRobert Mustacchi
58533affcbSRobert Mustacchi 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)59533affcbSRobert Mustacchi nvme_field_range_check(const nvme_field_info_t *field, uint64_t min,
60533affcbSRobert Mustacchi uint64_t max, char *msg, size_t msglen, uint64_t value)
61533affcbSRobert Mustacchi {
62533affcbSRobert Mustacchi if (value >= min && value <= max) {
63533affcbSRobert Mustacchi return (true);
64533affcbSRobert Mustacchi }
65533affcbSRobert Mustacchi
66533affcbSRobert Mustacchi (void) snprintf(msg, msglen, "field %s (%s) value 0x%"
67533affcbSRobert Mustacchi PRIx64 " is outside the valid range: [0x%" PRIx64 ", 0x%" PRIx64
68533affcbSRobert Mustacchi "]", field->nlfi_human, field->nlfi_spec, value, min, max);
69533affcbSRobert Mustacchi return (false);
70533affcbSRobert Mustacchi }
71533affcbSRobert Mustacchi
72533affcbSRobert Mustacchi /*
73533affcbSRobert Mustacchi * This is a general validation function for fields that are part of a command.
74533affcbSRobert Mustacchi * It will check if the field is supported by the controller and if so, that its
75533affcbSRobert Mustacchi * value is within the expected range. On error, an optional message will be
76533affcbSRobert Mustacchi * written that explains the error. This is intended to be shared between
77533affcbSRobert Mustacchi * userland and the kernel. The kernel should pass NULL/0 for msg/msglen because
78533affcbSRobert Mustacchi * there is no message translation capability in the kernel.
79533affcbSRobert Mustacchi */
80533affcbSRobert Mustacchi 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)81533affcbSRobert Mustacchi nvme_field_validate(const nvme_field_info_t *field,
82533affcbSRobert Mustacchi const nvme_valid_ctrl_data_t *data, uint64_t value, char *msg,
83533affcbSRobert Mustacchi size_t msglen)
84533affcbSRobert Mustacchi {
85533affcbSRobert Mustacchi ASSERT3P(field->nlfi_vers, !=, NULL);
86533affcbSRobert Mustacchi
87*7c801d36SAndy Fiddaman if (msglen > 0)
88*7c801d36SAndy Fiddaman *msg = '\0';
89*7c801d36SAndy Fiddaman
90533affcbSRobert Mustacchi if (!nvme_field_atleast(data, field->nlfi_vers)) {
91533affcbSRobert Mustacchi (void) snprintf(msg, msglen, "field %s (%s) requires "
92533affcbSRobert Mustacchi "version %u.%u, but device is at %u.%u", field->nlfi_human,
93533affcbSRobert Mustacchi field->nlfi_spec, data->vcd_vers->v_major,
94533affcbSRobert Mustacchi data->vcd_vers->v_minor, field->nlfi_vers->v_major,
95533affcbSRobert Mustacchi field->nlfi_vers->v_minor);
96533affcbSRobert Mustacchi return (NVME_FIELD_ERR_UNSUP_VERSION);
97533affcbSRobert Mustacchi }
98533affcbSRobert Mustacchi
99533affcbSRobert Mustacchi if (field->nlfi_sup != NULL && !field->nlfi_sup(field, data, msg,
100533affcbSRobert Mustacchi msglen)) {
101533affcbSRobert Mustacchi (void) snprintf(msg, msglen, "field %s (%s) is not "
102533affcbSRobert Mustacchi "supported by the controller", field->nlfi_human,
103533affcbSRobert Mustacchi field->nlfi_spec);
104533affcbSRobert Mustacchi return (NVME_FIELD_ERR_UNSUP_FIELD);
105533affcbSRobert Mustacchi }
106533affcbSRobert Mustacchi
107533affcbSRobert Mustacchi if (field->nlfi_valid != NULL) {
108533affcbSRobert Mustacchi if (!field->nlfi_valid(field, data, value, msg, msglen)) {
109*7c801d36SAndy Fiddaman if (msglen > 0 && *msg == '\0') {
110*7c801d36SAndy Fiddaman (void) snprintf(msg, msglen,
111*7c801d36SAndy Fiddaman "field %s (%s) value 0x%" PRIx64
112*7c801d36SAndy Fiddaman " is invalid",
113*7c801d36SAndy Fiddaman field->nlfi_human,
114533affcbSRobert Mustacchi field->nlfi_spec, value);
115*7c801d36SAndy Fiddaman }
116533affcbSRobert Mustacchi return (NVME_FIELD_ERR_BAD_VALUE);
117533affcbSRobert Mustacchi }
118533affcbSRobert Mustacchi } else if (!nvme_field_range_check(field, 0, field->nlfi_max_size, msg,
119533affcbSRobert Mustacchi msglen, value)) {
120533affcbSRobert Mustacchi return (NVME_FIELD_ERR_BAD_VALUE);
121533affcbSRobert Mustacchi }
122533affcbSRobert Mustacchi
123533affcbSRobert Mustacchi return (NVME_FIELD_ERR_OK);
124533affcbSRobert Mustacchi }
125