1#!/usr/bin/ksh
2#
3# This file and its contents are supplied under the terms of the
4# Common Development and Distribution License ("CDDL"), version 1.0.
5# You may only use this file in accordance with the terms of version
6# 1.0 of the CDDL.
7#
8# A full copy of the text of the CDDL should have accompanied this
9# source.  A copy of the CDDL is also available via the Internet at
10# http://www.illumos.org/license/CDDL.
11#
12
13#
14# Copyright 2024 Oxide Computer Company
15#
16
17#
18# This implements basic tests for nvmeadm(8). It expects to be passed a device
19# to operate on the same as the other non-destructive tests. It is important
20# that we only test non-destructive operations here. Even error paths for
21# destructive operations should be done elsewhere.
22#
23
24#
25# Set up the environment with a standard locale and debugging tools to help us
26# catch failures.
27#
28export LC_ALL=C.UTF-8
29export LD_PRELOAD=libumem.so
30export UMEM_DEBUG=default
31unalias -a
32set -o pipefail
33
34nt_prog=/usr/sbin/nvmeadm
35nt_arg0=$(basename $0)
36nt_exit=0
37nt_fail=0
38nt_dev="$NVME_TEST_DEVICE"
39nt_maj=
40nt_min=
41nt_ns=
42
43function warn
44{
45	typeset msg="$*"
46	[[ -z "$msg" ]] && msg="failed"
47	echo "TEST FAILED: $msg" >&2
48	nt_exit=1
49	((nt_fail++))
50}
51
52function fatal
53{
54        typeset msg="$*"
55        [[ -z "$msg" ]] && msg="failed"
56        echo "$nt_arg0: $msg" >&2
57        exit 1
58}
59
60#
61# Whether certain tests pass or fail depends on the version of the controller.
62# Capture that information now.
63#
64function capture_version
65{
66	typeset vers
67	typeset nsmgmt
68
69	vers=$($nt_prog list -p -o version $nt_dev)
70	if (( $? != 0 )); then
71		fatal "failed to capture NVMe version from $nt_dev"
72	fi
73
74	IFS=. read -r nt_maj nt_min <<< "$vers"
75
76	if [[ -z "$nt_maj" ]]; then
77		fatal "failed to parse NVMe major version from $vers"
78	fi
79
80	if [[ -z "$nt_min" ]]; then
81		fatal "failed to parse NVMe minor version from $vers"
82	fi
83
84	printf "Testing device %s: NVMe Version %u.%u\n" "$nt_dev" "$nt_maj" \
85	    "$nt_min"
86
87	nsmgmt=$(nvmeadm identify $nt_dev | awk \
88	    '/Namespace Management:/{ print $3 }')
89
90	if (( $? != 0 )); then
91		fatal "failed to determine Namespace Management support from" \
92	    "$nt_dev"
93	fi
94
95	if [[ -n "$nsmgmt" && "$nsmgmt" == "supported" ]]; then
96		nt_ns="yes"
97		printf "%s has namespace management\n" "$nt_dev"
98	else
99		printf "%s does not support namespace management\n" "$nt_dev"
100	fi
101}
102
103#
104# Run some program whose output we don't care about. Only whether it exits
105# successfully or not.
106#
107function nvmeadm_fail
108{
109	if "$nt_prog" $@ 2>/dev/null 1>/dev/null; then
110		warn "should have failed with args $@, but passed"
111		return;
112	fi
113
114	printf "TEST PASSED: program failed: %s\n" "$*"
115}
116
117#
118# Like the above, except we expect it to pass.
119#
120function nvmeadm_pass
121{
122	if ! "$nt_prog" $@ 2>/dev/null 1>/dev/null; then
123		warn "should have passed with args $@, but failed"
124		return;
125	fi
126
127	printf "TEST PASSED: %s %s exited successfully\n" "$nt_prog" "$*"
128}
129
130#
131# This is a test that we expect to pass based upon the version of the controller.
132#
133function nvmeadm_pv
134{
135	typeset vers="$1"
136	typeset maj min
137	shift
138
139	IFS=. read -r maj min <<< "$vers"
140	if [[ -z "$maj" ]]; then
141		fatal "failed to parse NVMe major version from test $vers"
142	fi
143
144	if [[ -z "$min" ]]; then
145		fatal "failed to parse NVMe minor version from test $vers"
146	fi
147
148	if (( nt_maj > maj || (nt_maj == maj && nt_min >= min) )); then
149		nvmeadm_pass $@
150	else
151		nvmeadm_fail $@
152	fi
153}
154
155#
156# Wrappers around nvmeadm_pv for the various versions we care about.
157#
158function nvmeadm_pv1v1
159{
160	nvmeadm_pv "1.1" $@
161}
162
163function nvmeadm_pv1v2
164{
165	nvmeadm_pv "1.2" $@
166}
167
168function nvmeadm_pv1v3
169{
170	nvmeadm_pv "1.3" $@
171}
172
173function nvmeadm_ns
174{
175	if [[ "$nt_ns" == "yes" ]]; then
176		nvmeadm_pv "1.2" $@
177	else
178		nvmeadm_fail $@
179	fi
180}
181
182if [[ -n "$NVMEADM" ]]; then
183	nt_prog="$NVMEADM"
184fi
185
186#
187# Note, we assume that the wrappers have already validated that this disk exists
188# for us. This tells us that we can expect a basic and specific list to work.
189#
190if [[ -z "$nt_dev" ]]; then
191	fatal "missing disk definition for \$NVME_TEST_DEVICE"
192fi
193
194capture_version
195
196#
197# Explicitly give bad arguments where our bad arguments aren't related to the
198# device.
199#
200nvmeadm_fail
201nvmeadm_fail foobar
202nvmeadm_fail list nvme
203nvmeadm_fail list nvmecloud
204nvmeadm_fail list nvmeterra
205nvmeadm_fail list nvme00
206nvmeadm_fail list nvme01
207nvmeadm_fail list nvme0x0
208nvmeadm_fail list nvme000
209nvmeadm_fail list nvme001
210nvmeadm_fail list nvme/1
211nvmeadm_fail list $nt_dev foobar
212nvmeadm_fail list $nt_dev,foobar
213nvmeadm_fail list $nt_dev/
214nvmeadm_fail list $nt_dev//1
215nvmeadm_fail list $nt_dev/terra
216nvmeadm_fail list $nt_dev/0
217nvmeadm_fail list $nt_dev/001
218nvmeadm_fail list $nt_dev/0x1
219nvmeadm_fail list $nt_dev/0o1
220nvmeadm_fail list $nt_dev/000
221nvmeadm_fail list $nt_dev/1asdf
222nvmeadm_fail list -p
223nvmeadm_fail list -o
224nvmeadm_fail list -o foobar
225nvmeadm_fail list -p -o foobar
226nvmeadm_fail list -p -o model,foobar
227nvmeadm_fail list -p -o model,foobar $nt_dev $nt_dev
228nvmeadm_fail list -p -o model,serial $nt_dev/1 $nt_dev
229nvmeadm_fail list -c $nt_dev foobar
230nvmeadm_fail list -c $nt_dev,foobar
231nvmeadm_fail list -c $nt_dev/
232nvmeadm_fail list -c $nt_dev/terra
233nvmeadm_fail list -c -p
234nvmeadm_fail list -c -o
235nvmeadm_fail list -c -o foobar
236nvmeadm_fail list -c -p -o foobar
237nvmeadm_fail list -c -p -o model,foobar
238nvmeadm_fail list -c -p -o model,foobar $nt_dev $nt_dev
239nvmeadm_fail list -c -p -o model,serial $nt_dev/1 $nt_dev
240nvmeadm_fail identify
241nvmeadm_fail identify -t
242nvmeadm_fail identify -C
243nvmeadm_fail identify -c
244nvmeadm_fail identify -n
245nvmeadm_fail identify -n -a
246nvmeadm_fail identify -a
247nvmeadm_fail identify -d
248nvmeadm_fail identify -d $nt_dev
249nvmeadm_fail identify -d $nt_dev/edgar
250nvmeadm_fail identify -d $nt_dev/1,$nt_dev/sabin
251nvmeadm_fail identify -C -a $nt_dev
252nvmeadm_fail identify -C -n $nt_dev
253nvmeadm_fail identify -c -a $nt_dev
254nvmeadm_fail identify -c -n $nt_dev
255nvmeadm_fail identify $nt_dev $nt_dev
256nvmeadm_fail identify-controller $nt_dev/1
257nvmeadm_fail identify-controller $nt_dev $nt_dev
258nvmeadm_fail identify-controller -x
259nvmeadm_fail identify-controller -C
260nvmeadm_fail identify-controller -c
261nvmeadm_fail identify-controller -n
262nvmeadm_fail identify-controller -n -a
263nvmeadm_fail identify-controller -a
264nvmeadm_fail identify-controller -C -a $nt_dev
265nvmeadm_fail identify-controller -C -n $nt_dev
266nvmeadm_fail identify-controller -c -n $nt_dev
267nvmeadm_fail identify-controller -c -a $nt_dev
268nvmeadm_fail identify-controller -C $nt_dev/1
269nvmeadm_fail identify-controller -c $nt_dev/1
270nvmeadm_fail identify-controller -n $nt_dev/1
271nvmeadm_fail identify-controller -n -a $nt_dev/1
272nvmeadm_fail identify-namespace
273nvmeadm_fail identify-namespace $nt_dev
274nvmeadm_fail identify-namespace $nt_dev/1 $nt_dev/1
275nvmeadm_fail identify-namespace -C
276nvmeadm_fail identify-namespace -d
277nvmeadm_fail identify-namespace -c
278nvmeadm_fail identify-namespace -C $nt_dev
279nvmeadm_fail identify-namespace -d $nt_dev
280nvmeadm_fail identify-namespace -c $nt_dev
281
282#
283# Log page tests are constrained to NVMe 1.0 features to keep things simpler.
284# See discussion in the pass section.
285#
286nvmeadm_fail list-logpages
287nvmeadm_fail list-logpages -a
288nvmeadm_fail list-logpages -H
289nvmeadm_fail list-logpages -p
290nvmeadm_fail list-logpages -p $nt_dev
291nvmeadm_fail list-logpages -p -o locke $nt_dev
292nvmeadm_fail list-logpages -o shadow $nt_dev
293nvmeadm_fail list-logpages -o device,shadow $nt_dev
294nvmeadm_fail list-logpages -s kefka $nt_dev
295nvmeadm_fail list-logpages -s nvm,kefka $nt_dev
296nvmeadm_fail list-logpages $nt_dev kefka
297nvmeadm_fail list-logpages $nt_dev health umaro
298nvmeadm_fail list-logpages $nt_dev/1 error
299nvmeadm_fail list-logpages $nt_dev/mog
300nvmeadm_fail list-logpages -s ns $nt_dev/1 firmware
301nvmeadm_fail get-logpage
302nvmeadm_fail get-logpage health
303nvmeadm_fail get-logpage health health
304nvmeadm_fail get-logpage $nt_dev
305nvmeadm_fail get-logpage $nt_dev,$nt_dev/1
306nvmeadm_fail get-logpage $nt_dev $nt_dev
307nvmeadm_fail get-logpage $nt_dev sephiorth
308nvmeadm_fail get-logpage $nt_dev health sephiorth
309nvmeadm_fail get-logpage $nt_dev health,error
310nvmeadm_fail list-features
311nvmeadm_fail list-features -a
312nvmeadm_fail list-features -o csi
313nvmeadm_fail list-features -H
314nvmeadm_fail list-features -p
315nvmeadm_fail list-features -p $nt_dev
316nvmeadm_fail list-features -o magicite $nt_dev
317nvmeadm_fail list-features -o csi materia
318nvmeadm_fail list-features $nt_dev $nt_dev
319nvmeadm_fail list-features $nt_dev/1 $nt_dev/1
320nvmeadm_fail list-features $nt_dev/1 aerith
321nvmeadm_fail list-features $nt_dev aerith arb temp
322nvmeadm_fail get-features
323nvmeadm_fail get-features $nt_dev/cid
324nvmeadm_fail get-features vincent
325nvmeadm_fail get-features $nt_dev range
326nvmeadm_fail get-features $nt_dev/1 temp
327nvmeadm_fail get-features $nt_dev temp,tifa
328nvmeadm_fail get-features $nt_dev temp cloud
329
330nvmeadm_fail list-firmware
331nvmeadm_fail list-firmware -t
332nvmeadm_fail list-firmware $nt_dev/1
333nvmeadm_fail list-firmware $nt_dev/squall
334nvmeadm_fail list-firmware $nt_dev rinoa
335
336#
337# Tests that we expect to pass in some form.
338#
339nvmeadm_pass list
340nvmeadm_pass list $nt_dev
341nvmeadm_pass list $nt_dev/1
342nvmeadm_pass list $nt_dev,$nt_dev/1
343nvmeadm_pass list -p -o model,serial $nt_dev
344nvmeadm_pass list -p -o model,serial $nt_dev/1
345nvmeadm_pass list -p -o model,serial $nt_dev/1,$nt_dev
346nvmeadm_pass list -c
347nvmeadm_pass list -c $nt_dev
348nvmeadm_pass list -c $nt_dev/1
349nvmeadm_pass list -c $nt_dev,$nt_dev/1
350nvmeadm_pass list -c -p -o model,serial $nt_dev
351nvmeadm_pass list -c -p -o model,serial $nt_dev/1
352nvmeadm_pass list -c -p -o model,serial $nt_dev/1,$nt_dev
353nvmeadm_pass identify $nt_dev
354nvmeadm_pass identify $nt_dev/1
355nvmeadm_pass identify $nt_dev,$nt_dev/1
356nvmeadm_pass identify $nt_dev,$nt_dev
357nvmeadm_pass identify $nt_dev,$nt_dev/1,$nt_dev
358nvmeadm_ns identify -C $nt_dev
359nvmeadm_ns identify -c $nt_dev
360nvmeadm_pv1v3 identify -d $nt_dev/1
361nvmeadm_pv1v1 identify -n $nt_dev
362nvmeadm_ns identify -n -a $nt_dev
363nvmeadm_ns identify-controller -C $nt_dev
364nvmeadm_ns identify-controller -c $nt_dev
365nvmeadm_pv1v1 identify-controller -n $nt_dev
366nvmeadm_ns identify-controller -n -a $nt_dev
367
368#
369# All of our list- and get-logpage tests strictly use the 3 NVMe 1.0 log pages:
370# error, health, firmware. Note: NVMe 1.0 did not define any namespace-specific
371# log pages, the health log page was changed later. Therefore there are no tests
372# specific to solely namespace scope right now.
373#
374nvmeadm_pass list-logpages $nt_dev
375nvmeadm_pass list-logpages -H $nt_dev
376nvmeadm_pass list-logpages -o device $nt_dev
377nvmeadm_pass list-logpages -H -o device $nt_dev
378nvmeadm_pass list-logpages -o \
379    device,name,desc,scope,fields,csi,lid,impl,size,minsize,sources,kind $nt_dev
380nvmeadm_pass list-logpages -p -o lid $nt_dev
381nvmeadm_pass list-logpages -H -p -o lid $nt_dev
382nvmeadm_pass list-logpages -s controller $nt_dev
383nvmeadm_pass list-logpages -s controller -H $nt_dev
384nvmeadm_pass list-logpages -s controller -H -o device $nt_dev
385nvmeadm_pass list-logpages -s controller -o \
386    device,name,desc,scope,fields,csi,lid,impl,size,minsize,sources,kind $nt_dev
387nvmeadm_pass list-logpages -s controller -p -o lid $nt_dev
388nvmeadm_pass list-logpages -s controller,nvm $nt_dev
389nvmeadm_pass list-logpages -s controller,nvm,ns $nt_dev
390nvmeadm_pass list-logpages $nt_dev,$nt_dev
391nvmeadm_pass list-logpages $nt_dev error
392nvmeadm_pass list-logpages $nt_dev health
393nvmeadm_pass list-logpages -a $nt_dev
394nvmeadm_pass list-logpages -a $nt_dev health
395nvmeadm_pass list-logpages -s controller $nt_dev health
396nvmeadm_pass get-logpage $nt_dev health
397nvmeadm_pass get-logpage $nt_dev firmware
398nvmeadm_pass get-logpage $nt_dev,$nt_dev firmware
399nvmeadm_pass list-features $nt_dev
400nvmeadm_pass list-features -H $nt_dev
401nvmeadm_pass list-features -o device $nt_dev
402nvmeadm_pass list-features -p -o device,impl $nt_dev
403nvmeadm_pass list-features -o \
404    device,short,spec,fid,scope,kind,csi,flags,get-in,get-out,set-out,datalen \
405    $nt_dev
406nvmeadm_pass list-features -a $nt_dev
407nvmeadm_pass list-features $nt_dev/1
408nvmeadm_pass list-features -H $nt_dev/1
409nvmeadm_pass list-features -o short,fid $nt_dev/1
410nvmeadm_pass list-features -p -o device,impl $nt_dev/1
411nvmeadm_pass list-features -o \
412    device,short,spec,fid,scope,kind,csi,flags,get-in,get-out,set-out,datalen \
413    $nt_dev/1
414nvmeadm_pass list-features -a $nt_dev/1
415nvmeadm_pass list-features $nt_dev,$nt_dev/1
416nvmeadm_pass list-features $nt_dev,$nt_dev/1,$nt_dev
417nvmeadm_pass list-features $nt_dev pm
418nvmeadm_pass list-features $nt_dev arbitration
419nvmeadm_pass list-features $nt_dev Arbitration
420nvmeadm_pass list-features $nt_dev pm Arbitration temp
421#
422# If you find a get-features without arguments is causing issues, please feel
423# free to remove this test.
424#
425nvmeadm_pass get-features $nt_dev
426nvmeadm_pass get-features $nt_dev temp
427nvmeadm_pass get-features $nt_dev temp,vector
428nvmeadm_pass get-features $nt_dev,$nt_dev arb
429
430nvmeadm_pass list-firmware $nt_dev
431
432#
433# Here we come back and use bad controller names that should not work.
434# These specifically do not rely on $nt_dev.
435#
436
437if (( nt_exit == 0 )); then
438	printf "All tests passed successfully!\n"
439else
440	printf "%u tests failed!\n" "$nt_fail"
441fi
442
443exit $nt_exit
444