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  * Perform basic GET LOG PAGE commands just to prove that this works end to
18  * work. This is not intended to be an exhaustive set of tests, merely proving
19  * that things work. We utilize the SMART / Health information and the Firmware
20  * Slot information because these two pages have existed since NVMe 1.0 and both
21  * are the same fixed size (512 bytes). They both have useful pieces of expected
22  * non-zero data: the current temperature and firmware slot respectively. These
23  * are both within the first u32, so we just check that the entire u32 is
24  * non-zero and not all 1s.
25  *
26  * It's quite probably virtualized devices will lie about these. When they do,
27  * we should add specific devices to an exception list.
28  */
29 
30 #include <err.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/sysmacros.h>
35 
36 #include "nvme_ioctl_util.h"
37 
38 typedef struct {
39 	const char *lt_desc;
40 	uint32_t lt_lid;
41 	size_t lt_len;
42 } log_test_t;
43 
44 static const log_test_t basic_log_tests[] = {
45 	{ "Health/SMART log page", NVME_LOGPAGE_HEALTH, 512 },
46 	{ "Firmware log page", NVME_LOGPAGE_FWSLOT, 512 },
47 };
48 
49 static bool
basic_get_one_log(int fd,const log_test_t * test)50 basic_get_one_log(int fd, const log_test_t *test)
51 {
52 	bool ret = true;
53 	nvme_ioctl_get_logpage_t log;
54 	void *data;
55 
56 	if ((data = calloc(test->lt_len, 1)) == NULL) {
57 		err(EXIT_FAILURE, "%s: failed to allocate %zu bytes for "
58 		    "log page data", test->lt_desc, test->lt_len);
59 	}
60 
61 	(void) memset(&log, 0, sizeof (log));
62 	log.nigl_csi = NVME_CSI_NVM;
63 	log.nigl_lid = test->lt_lid;
64 	log.nigl_len = test->lt_len;
65 	log.nigl_data = (uintptr_t)data;
66 
67 	if (ioctl(fd, NVME_IOC_GET_LOGPAGE, &log) != 0) {
68 		warn("TEST FAILED: %s: failed to issue get log page ioctl",
69 		    test->lt_desc);
70 		ret = false;
71 	} else if (log.nigl_common.nioc_drv_err != NVME_IOCTL_E_OK) {
72 		warnx("TEST FAILED: %s: get log page ioctl set error to 0x%x, "
73 		    "but expected success", test->lt_desc,
74 		    log.nigl_common.nioc_drv_err);
75 		ret = false;
76 	} else {
77 		uint32_t t;
78 
79 		(void) printf("TEST PASSED: %s: successfully issued get log "
80 		    "page command\n", test->lt_desc);
81 		(void) memcpy(&t, data, sizeof (t));
82 		if (t == 0 || t == UINT32_MAX) {
83 			warnx("TEST FAILED: %s: uint32_t at word 0 looks like "
84 			    "invalid data, found 0x%x", test->lt_desc, t);
85 			ret = false;
86 		} else {
87 			(void) printf("TEST PASSED: %s: returned data passes "
88 			    "initial scrutiny\n", test->lt_desc);
89 		}
90 	}
91 
92 	free(data);
93 	return (ret);
94 }
95 
96 int
main(void)97 main(void)
98 {
99 	int ret = EXIT_SUCCESS;
100 	int fd = nvme_ioctl_test_get_fd(0);
101 
102 	for (size_t i = 0; i < ARRAY_SIZE(basic_log_tests); i++) {
103 		if (!basic_get_one_log(fd, &basic_log_tests[i])) {
104 			ret = EXIT_FAILURE;
105 		}
106 	}
107 
108 	VERIFY0(close(fd));
109 	return (ret);
110 }
111