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 2026 Oxide Computer Company 14 */ 15 16 /* 17 * Read and write to the AMD SMN. 18 */ 19 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <unistd.h> 23 #include <err.h> 24 #include <sys/types.h> 25 #include <sys/stat.h> 26 #include <fcntl.h> 27 #include <errno.h> 28 #include <usmn.h> 29 30 #include "amdzen_common.h" 31 32 static boolean_t 33 usmn_parse_uint32(const char *str, uint32_t *valp) 34 { 35 long long l; 36 char *eptr; 37 38 errno = 0; 39 l = strtoll(str, &eptr, 16); 40 if (errno != 0 || *eptr != '\0') { 41 warnx("failed to parse string '%s'", str); 42 return (B_FALSE); 43 } 44 45 if (l < 0 || l > UINT32_MAX) { 46 warnx("value %s is outside the valid range [0, UINT32_MAX]", 47 str); 48 return (B_FALSE); 49 } 50 51 *valp = (uint32_t)l; 52 return (B_TRUE); 53 } 54 55 static boolean_t 56 usmn_op(boolean_t do_write, int fd, const char *addr, uint32_t length, 57 uint32_t value) 58 { 59 usmn_reg_t usr; 60 61 usr.usr_data = value; 62 usr.usr_size = length; 63 if (!usmn_parse_uint32(addr, &usr.usr_addr)) { 64 return (B_FALSE); 65 } 66 67 if (ioctl(fd, do_write ? USMN_WRITE : USMN_READ, &usr) != 0) { 68 warn("SMN ioctl failed at 0x%x", usr.usr_addr); 69 return (B_FALSE); 70 } 71 72 if (!do_write) { 73 (void) printf("0x%x: 0x%x\n", usr.usr_addr, usr.usr_data); 74 } 75 return (B_TRUE); 76 } 77 78 int 79 main(int argc, char *argv[]) 80 { 81 int i, c, fd, ret; 82 const char *device = NULL; 83 boolean_t do_write = B_FALSE; 84 uint32_t wval = 0; 85 uint32_t length = 4; 86 87 while ((c = getopt(argc, argv, "d:L:w:")) != -1) { 88 switch (c) { 89 case 'd': 90 device = optarg; 91 break; 92 case 'L': 93 if (!usmn_parse_uint32(optarg, &length)) { 94 return (EXIT_FAILURE); 95 } 96 if (length != 1 && length != 2 && length != 4) { 97 warnx("length %u is out of range {1,2,4}", 98 length); 99 return (EXIT_FAILURE); 100 } 101 break; 102 case 'w': 103 do_write = B_TRUE; 104 if (!usmn_parse_uint32(optarg, &wval)) { 105 return (EXIT_FAILURE); 106 } 107 break; 108 default: 109 (void) fprintf(stderr, "Usage: usmn -d device " 110 "[-L length] [-w value] addr [addr]...\n" 111 "Note: All addresses are interpreted as hex\n"); 112 return (EXIT_USAGE); 113 } 114 } 115 116 if (device == NULL) { 117 errx(EXIT_FAILURE, "missing required device"); 118 } 119 120 argc -= optind; 121 argv += optind; 122 123 if (argc == 0) { 124 errx(EXIT_FAILURE, "at least one register must be specified"); 125 } 126 127 if (do_write && argc != 1) { 128 errx(EXIT_FAILURE, "can only write to a single register"); 129 } 130 131 fd = amdzen_open_device("usmn", device, do_write ? O_RDWR : O_RDONLY); 132 if (fd < 0) 133 return (EXIT_FAILURE); 134 135 ret = EXIT_SUCCESS; 136 for (i = 0; i < argc; i++) { 137 if (!usmn_op(do_write, fd, argv[i], length, wval)) { 138 ret = EXIT_FAILURE; 139 } 140 } 141 142 (void) close(fd); 143 return (ret); 144 } 145