18d55b806SRobert Mustacchi /*
28d55b806SRobert Mustacchi  * This file and its contents are supplied under the terms of the
38d55b806SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
48d55b806SRobert Mustacchi  * You may only use this file in accordance with the terms of version
58d55b806SRobert Mustacchi  * 1.0 of the CDDL.
68d55b806SRobert Mustacchi  *
78d55b806SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
88d55b806SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
98d55b806SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
108d55b806SRobert Mustacchi  */
118d55b806SRobert Mustacchi 
128d55b806SRobert Mustacchi /*
13*499bc737SRobert Mustacchi  * Copyright 2022 Oxide Computer Company
148d55b806SRobert Mustacchi  */
158d55b806SRobert Mustacchi 
168d55b806SRobert Mustacchi /*
17bbf21555SRichard Lowe  * fwflash(8) backend for UFMs.
188d55b806SRobert Mustacchi  */
198d55b806SRobert Mustacchi 
208d55b806SRobert Mustacchi #include <libdevinfo.h>
218d55b806SRobert Mustacchi #include <strings.h>
228d55b806SRobert Mustacchi #include <libintl.h>
238d55b806SRobert Mustacchi #include <pcidb.h>
248d55b806SRobert Mustacchi #include <sys/types.h>
258d55b806SRobert Mustacchi #include <sys/stat.h>
268d55b806SRobert Mustacchi #include <fcntl.h>
278d55b806SRobert Mustacchi #include <unistd.h>
288d55b806SRobert Mustacchi #include <libnvpair.h>
298d55b806SRobert Mustacchi #include <sys/ddi_ufm.h>
308d55b806SRobert Mustacchi #include <sys/sysmacros.h>
318d55b806SRobert Mustacchi #include <fwflash/fwflash.h>
328d55b806SRobert Mustacchi 
338d55b806SRobert Mustacchi /*
348d55b806SRobert Mustacchi  * We pick a fixed size unit to work on.
358d55b806SRobert Mustacchi  */
368d55b806SRobert Mustacchi #define	UFM_READ_BUFLEN	(16 * 1024 * 1024)
378d55b806SRobert Mustacchi 
388d55b806SRobert Mustacchi /*
398d55b806SRobert Mustacchi  * These are indexes into the addresses array that we use.
408d55b806SRobert Mustacchi  */
418d55b806SRobert Mustacchi #define	UFM_ADDR_PATH	0
428d55b806SRobert Mustacchi #define	UFM_ADDR_SUB	1
438d55b806SRobert Mustacchi #define	UFM_ADDR_CAP	2
448d55b806SRobert Mustacchi 
458d55b806SRobert Mustacchi typedef struct ufmfw_ident_arg {
468d55b806SRobert Mustacchi 	uint_t uia_nfound;
478d55b806SRobert Mustacchi 	int uia_index;
488d55b806SRobert Mustacchi 	int uia_err;
498d55b806SRobert Mustacchi } ufmfw_ident_arg_t;
508d55b806SRobert Mustacchi 
518d55b806SRobert Mustacchi /*
528d55b806SRobert Mustacchi  * fwflash requires we declare our driver name as data with this name.
538d55b806SRobert Mustacchi  */
548d55b806SRobert Mustacchi const char drivername[] = "ufm";
558d55b806SRobert Mustacchi const int plugin_version = FWPLUGIN_VERSION_2;
568d55b806SRobert Mustacchi 
578d55b806SRobert Mustacchi /*
588d55b806SRobert Mustacchi  * External data from fwflash.
598d55b806SRobert Mustacchi  */
608d55b806SRobert Mustacchi extern di_node_t rootnode;
618d55b806SRobert Mustacchi extern struct fw_plugin *self;
628d55b806SRobert Mustacchi 
638d55b806SRobert Mustacchi /*
648d55b806SRobert Mustacchi  * Global, shared data.
658d55b806SRobert Mustacchi  */
668d55b806SRobert Mustacchi static int ufmfw_ufm_fd = -1;
678d55b806SRobert Mustacchi static pcidb_hdl_t *ufmfw_pcidb;
688d55b806SRobert Mustacchi static boolean_t ufmfw_ready = B_FALSE;
698d55b806SRobert Mustacchi 
708d55b806SRobert Mustacchi /*
718d55b806SRobert Mustacchi  * Read image zero and slot zero that we find.
728d55b806SRobert Mustacchi  */
738d55b806SRobert Mustacchi int
fw_readfw(struct devicelist * flashdev,const char * filename)748d55b806SRobert Mustacchi fw_readfw(struct devicelist *flashdev, const char *filename)
758d55b806SRobert Mustacchi {
768d55b806SRobert Mustacchi 	nvlist_t **images, **slots;
778d55b806SRobert Mustacchi 	uint_t nimages, nslots, caps;
788d55b806SRobert Mustacchi 	uint64_t imgsize, offset;
798d55b806SRobert Mustacchi 	void *buf;
808d55b806SRobert Mustacchi 	int fd;
818d55b806SRobert Mustacchi 	nvlist_t *nvl = flashdev->ident->encap_ident;
828d55b806SRobert Mustacchi 
838d55b806SRobert Mustacchi 	caps = (uintptr_t)flashdev->addresses[UFM_ADDR_CAP];
848d55b806SRobert Mustacchi 	if ((caps & DDI_UFM_CAP_READIMG) == 0) {
858d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, "%s: device %s does not support reading "
868d55b806SRobert Mustacchi 		    "images\n", flashdev->drvname, flashdev->access_devname);
878d55b806SRobert Mustacchi 		return (FWFLASH_FAILURE);
888d55b806SRobert Mustacchi 	}
898d55b806SRobert Mustacchi 
908d55b806SRobert Mustacchi 	if (nvlist_lookup_nvlist_array(nvl, DDI_UFM_NV_IMAGES, &images,
918d55b806SRobert Mustacchi 	    &nimages) != 0) {
928d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: %s missing UFM image data\n"),
938d55b806SRobert Mustacchi 		    flashdev->drvname, flashdev->access_devname);
948d55b806SRobert Mustacchi 		return (FWFLASH_FAILURE);
958d55b806SRobert Mustacchi 	}
968d55b806SRobert Mustacchi 
978d55b806SRobert Mustacchi 	if (nimages == 0) {
988d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: %s has no UFM images\n"),
998d55b806SRobert Mustacchi 		    flashdev->drvname, flashdev->access_devname);
1008d55b806SRobert Mustacchi 		return (FWFLASH_FAILURE);
1018d55b806SRobert Mustacchi 	}
1028d55b806SRobert Mustacchi 
1038d55b806SRobert Mustacchi 	if (nvlist_lookup_nvlist_array(images[0], DDI_UFM_NV_IMAGE_SLOTS,
1048d55b806SRobert Mustacchi 	    &slots, &nslots) != 0) {
1058d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: image zero of %s has no "
1068d55b806SRobert Mustacchi 		    "slots\n"), flashdev->drvname, flashdev->access_devname);
1078d55b806SRobert Mustacchi 		return (FWFLASH_FAILURE);
1088d55b806SRobert Mustacchi 	}
1098d55b806SRobert Mustacchi 
1108d55b806SRobert Mustacchi 	if (nvlist_lookup_uint64(slots[0], DDI_UFM_NV_SLOT_IMGSIZE,
1118d55b806SRobert Mustacchi 	    &imgsize) != 0) {
1128d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: device %s doesn't have an image "
1138d55b806SRobert Mustacchi 		    "size\n"), flashdev->drvname, flashdev->access_devname);
1148d55b806SRobert Mustacchi 		return (FWFLASH_FAILURE);
1158d55b806SRobert Mustacchi 	}
1168d55b806SRobert Mustacchi 
1178d55b806SRobert Mustacchi 	logmsg(MSG_INFO, gettext("%s: Need to read %" PRIu64 " bytes\n"),
1188d55b806SRobert Mustacchi 	    flashdev->drvname, imgsize);
1198d55b806SRobert Mustacchi 
1208d55b806SRobert Mustacchi 	if ((buf = malloc(UFM_READ_BUFLEN)) == NULL) {
1218d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: Failed to allocate data "
1228d55b806SRobert Mustacchi 		    "buffer\n"), flashdev->drvname);
1238d55b806SRobert Mustacchi 		return (FWFLASH_FAILURE);
1248d55b806SRobert Mustacchi 	}
1258d55b806SRobert Mustacchi 
1268d55b806SRobert Mustacchi 	if ((fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0644)) < 0) {
1278d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to open file %s: %s\n"),
1288d55b806SRobert Mustacchi 		    flashdev->drvname, filename, strerror(errno));
1298d55b806SRobert Mustacchi 		free(buf);
1308d55b806SRobert Mustacchi 		return (FWFLASH_FAILURE);
1318d55b806SRobert Mustacchi 	}
1328d55b806SRobert Mustacchi 
1338d55b806SRobert Mustacchi 	offset = 0;
1348d55b806SRobert Mustacchi 	while (imgsize > 0) {
1358d55b806SRobert Mustacchi 		ufm_ioc_readimg_t rimg;
1368d55b806SRobert Mustacchi 		uint64_t toread = MIN(imgsize, UFM_READ_BUFLEN);
1378d55b806SRobert Mustacchi 		size_t woff;
1388d55b806SRobert Mustacchi 
1398d55b806SRobert Mustacchi 		bzero(&rimg, sizeof (rimg));
1408d55b806SRobert Mustacchi 		rimg.ufri_version = DDI_UFM_CURRENT_VERSION;
1418d55b806SRobert Mustacchi 		rimg.ufri_imageno = 0;
1428d55b806SRobert Mustacchi 		rimg.ufri_slotno = 0;
1438d55b806SRobert Mustacchi 		rimg.ufri_offset = offset;
1448d55b806SRobert Mustacchi 		rimg.ufri_len = toread;
1458d55b806SRobert Mustacchi 		rimg.ufri_buf = buf;
1468d55b806SRobert Mustacchi 		(void) strlcpy(rimg.ufri_devpath,
1478d55b806SRobert Mustacchi 		    flashdev->addresses[UFM_ADDR_PATH],
1488d55b806SRobert Mustacchi 		    sizeof (rimg.ufri_devpath));
1498d55b806SRobert Mustacchi 		logmsg(MSG_INFO, gettext("%s: want to read %" PRIu64 " bytes "
1508d55b806SRobert Mustacchi 		    "at offset %" PRIu64 "\n"), flashdev->drvname,
1518d55b806SRobert Mustacchi 		    rimg.ufri_len, rimg.ufri_offset);
1528d55b806SRobert Mustacchi 
1538d55b806SRobert Mustacchi 		if (ioctl(ufmfw_ufm_fd, UFM_IOC_READIMG, &rimg) != 0) {
1548d55b806SRobert Mustacchi 			logmsg(MSG_ERROR, gettext("%s: failed to read image: "
1558d55b806SRobert Mustacchi 			    "%s\n"), flashdev->drvname, strerror(errno));
1568d55b806SRobert Mustacchi 			free(buf);
1578d55b806SRobert Mustacchi 			(void) close(fd);
1588d55b806SRobert Mustacchi 			return (FWFLASH_FAILURE);
1598d55b806SRobert Mustacchi 		}
1608d55b806SRobert Mustacchi 
1618d55b806SRobert Mustacchi 		logmsg(MSG_INFO, gettext("%s: read %" PRIu64 " bytes at offset "
1628d55b806SRobert Mustacchi 		    "%" PRIu64 "\n"), flashdev->drvname, rimg.ufri_nread,
1638d55b806SRobert Mustacchi 		    offset);
1648d55b806SRobert Mustacchi 		offset += rimg.ufri_nread;
1658d55b806SRobert Mustacchi 		imgsize -= rimg.ufri_nread;
1668d55b806SRobert Mustacchi 
1678d55b806SRobert Mustacchi 		woff = 0;
1688d55b806SRobert Mustacchi 		while (rimg.ufri_nread > 0) {
1698d55b806SRobert Mustacchi 			size_t towrite = MIN(rimg.ufri_nread, UFM_READ_BUFLEN);
1708d55b806SRobert Mustacchi 			ssize_t ret = write(fd, buf + woff, towrite);
1718d55b806SRobert Mustacchi 			if (ret == -1) {
1728d55b806SRobert Mustacchi 				logmsg(MSG_ERROR, gettext("%s: failed to write "
1738d55b806SRobert Mustacchi 				    "to %s: %s\n"), flashdev->drvname, filename,
1748d55b806SRobert Mustacchi 				    strerror(errno));
1758d55b806SRobert Mustacchi 				free(buf);
1768d55b806SRobert Mustacchi 				(void) close(fd);
1778d55b806SRobert Mustacchi 				return (FWFLASH_FAILURE);
1788d55b806SRobert Mustacchi 			}
1798d55b806SRobert Mustacchi 
1808d55b806SRobert Mustacchi 			rimg.ufri_nread -= ret;
1818d55b806SRobert Mustacchi 			woff += ret;
1828d55b806SRobert Mustacchi 		}
1838d55b806SRobert Mustacchi 	}
1848d55b806SRobert Mustacchi 
1858d55b806SRobert Mustacchi 	free(buf);
1868d55b806SRobert Mustacchi 	if (close(fd) != 0) {
1878d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to finish writing to %s: "
1888d55b806SRobert Mustacchi 		    "%s\n"), flashdev->drvname, filename, strerror(errno));
1898d55b806SRobert Mustacchi 		return (FWFLASH_FAILURE);
1908d55b806SRobert Mustacchi 	}
1918d55b806SRobert Mustacchi 	logmsg(MSG_INFO, gettext("%s: successfully wrote image to %s\n"),
1928d55b806SRobert Mustacchi 	    flashdev->drvname, filename);
1938d55b806SRobert Mustacchi 	return (FWFLASH_SUCCESS);
1948d55b806SRobert Mustacchi }
1958d55b806SRobert Mustacchi 
1968d55b806SRobert Mustacchi 
1978d55b806SRobert Mustacchi int
fw_writefw(struct devicelist * flashdev)1988d55b806SRobert Mustacchi fw_writefw(struct devicelist *flashdev)
1998d55b806SRobert Mustacchi {
2008d55b806SRobert Mustacchi 	return (FWFLASH_SUCCESS);
2018d55b806SRobert Mustacchi }
2028d55b806SRobert Mustacchi 
2038d55b806SRobert Mustacchi static void
ufmfw_flashdev_free(struct devicelist * flashdev)2048d55b806SRobert Mustacchi ufmfw_flashdev_free(struct devicelist *flashdev)
2058d55b806SRobert Mustacchi {
2068d55b806SRobert Mustacchi 	if (flashdev == NULL)
2078d55b806SRobert Mustacchi 		return;
2088d55b806SRobert Mustacchi 	if (flashdev->ident != NULL) {
2098d55b806SRobert Mustacchi 		free(flashdev->ident->vid);
2108d55b806SRobert Mustacchi 		free(flashdev->ident->pid);
2118d55b806SRobert Mustacchi 		nvlist_free(flashdev->ident->encap_ident);
2128d55b806SRobert Mustacchi 	}
2138d55b806SRobert Mustacchi 	free(flashdev->ident);
2148d55b806SRobert Mustacchi 	free(flashdev->drvname);
2158d55b806SRobert Mustacchi 	free(flashdev->classname);
2168d55b806SRobert Mustacchi 	free(flashdev->access_devname);
2178d55b806SRobert Mustacchi 	di_devfs_path_free(flashdev->addresses[UFM_ADDR_PATH]);
2188d55b806SRobert Mustacchi 	free(flashdev->addresses[UFM_ADDR_SUB]);
2198d55b806SRobert Mustacchi 	free(flashdev);
2208d55b806SRobert Mustacchi }
2218d55b806SRobert Mustacchi 
2228d55b806SRobert Mustacchi /*
2238d55b806SRobert Mustacchi  * Check if a node is a PCI device. This is so we can deal with VPD information.
2248d55b806SRobert Mustacchi  * Hopefully we'll have a generalized devinfo or fmtopo VPD section which we can
2258d55b806SRobert Mustacchi  * then use for this instead.
2268d55b806SRobert Mustacchi  */
2278d55b806SRobert Mustacchi static boolean_t
ufmfw_node_pci(di_node_t node)2288d55b806SRobert Mustacchi ufmfw_node_pci(di_node_t node)
2298d55b806SRobert Mustacchi {
2308d55b806SRobert Mustacchi 	while (node != DI_NODE_NIL) {
2318d55b806SRobert Mustacchi 		char *strs;
2328d55b806SRobert Mustacchi 		int ret = di_prop_lookup_strings(DDI_DEV_T_ANY, node,
2338d55b806SRobert Mustacchi 		    "device_type", &strs);
2348d55b806SRobert Mustacchi 
2358d55b806SRobert Mustacchi 		if (ret > 0) {
2368d55b806SRobert Mustacchi 			if (strcmp(strs, "pci") == 0 ||
2378d55b806SRobert Mustacchi 			    strcmp(strs, "pciex") == 0) {
2388d55b806SRobert Mustacchi 				return (B_TRUE);
2398d55b806SRobert Mustacchi 			}
2408d55b806SRobert Mustacchi 		}
2418d55b806SRobert Mustacchi 
2428d55b806SRobert Mustacchi 		node = di_parent_node(node);
2438d55b806SRobert Mustacchi 	}
2448d55b806SRobert Mustacchi 	return (B_FALSE);
2458d55b806SRobert Mustacchi }
2468d55b806SRobert Mustacchi 
2478d55b806SRobert Mustacchi /*
2488d55b806SRobert Mustacchi  * Cons up VPD information based on the PCI ID. Hopefully in time we'll use the
2498d55b806SRobert Mustacchi  * actual PCI VPD information and more generally allow a device to specify its
2508d55b806SRobert Mustacchi  * vpd automatically.
2518d55b806SRobert Mustacchi  */
2528d55b806SRobert Mustacchi static boolean_t
ufmfw_fill_vpd(struct devicelist * flashdev,di_node_t node)2538d55b806SRobert Mustacchi ufmfw_fill_vpd(struct devicelist *flashdev, di_node_t node)
2548d55b806SRobert Mustacchi {
2558d55b806SRobert Mustacchi 	int *vid, *did, *svid, *sdid;
2568d55b806SRobert Mustacchi 	pcidb_vendor_t *vend = NULL;
2578d55b806SRobert Mustacchi 	pcidb_device_t *dev = NULL;
2588d55b806SRobert Mustacchi 	pcidb_subvd_t *subdv = NULL;
2598d55b806SRobert Mustacchi 	char *vstr, *dstr;
2608d55b806SRobert Mustacchi 
2618d55b806SRobert Mustacchi 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "vendor-id", &vid) != 1) {
2628d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: %s missing 'vendor-id' "
2638d55b806SRobert Mustacchi 		    "property\n"), flashdev->drvname, flashdev->access_devname);
2648d55b806SRobert Mustacchi 		return (B_FALSE);
2658d55b806SRobert Mustacchi 	}
2668d55b806SRobert Mustacchi 
2678d55b806SRobert Mustacchi 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-id", &did) != 1) {
2688d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: %s missing 'device-id' "
2698d55b806SRobert Mustacchi 		    "property\n"), flashdev->drvname, flashdev->access_devname);
2708d55b806SRobert Mustacchi 		return (B_FALSE);
2718d55b806SRobert Mustacchi 	}
2728d55b806SRobert Mustacchi 
2738d55b806SRobert Mustacchi 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "subsystem-vendor-id",
2748d55b806SRobert Mustacchi 	    &svid) != 1 || di_prop_lookup_ints(DDI_DEV_T_ANY, node,
2758d55b806SRobert Mustacchi 	    "subsystem-device-id", &sdid) != 1) {
2768d55b806SRobert Mustacchi 		svid = NULL;
2778d55b806SRobert Mustacchi 		sdid = NULL;
2788d55b806SRobert Mustacchi 	}
2798d55b806SRobert Mustacchi 
2808d55b806SRobert Mustacchi 	vend = pcidb_lookup_vendor(ufmfw_pcidb, vid[0]);
2818d55b806SRobert Mustacchi 	if (vend != NULL) {
2828d55b806SRobert Mustacchi 		dev = pcidb_lookup_device_by_vendor(vend, did[0]);
2838d55b806SRobert Mustacchi 	}
2848d55b806SRobert Mustacchi 
2858d55b806SRobert Mustacchi 	if (dev != NULL && svid != NULL && sdid != NULL) {
2868d55b806SRobert Mustacchi 		subdv = pcidb_lookup_subvd_by_device(dev, svid[0], sdid[0]);
2878d55b806SRobert Mustacchi 	}
2888d55b806SRobert Mustacchi 
2898d55b806SRobert Mustacchi 	if (vend != NULL) {
2908d55b806SRobert Mustacchi 		vstr = strdup(pcidb_vendor_name(vend));
2918d55b806SRobert Mustacchi 	} else {
2928d55b806SRobert Mustacchi 		(void) asprintf(&vstr, "pci:%x", vid[0]);
2938d55b806SRobert Mustacchi 	}
2948d55b806SRobert Mustacchi 
2958d55b806SRobert Mustacchi 	if (vstr == NULL) {
2968d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to allocate vid "
2978d55b806SRobert Mustacchi 		    "string\n"), flashdev->drvname);
2988d55b806SRobert Mustacchi 		return (B_FALSE);
2998d55b806SRobert Mustacchi 	}
3008d55b806SRobert Mustacchi 	flashdev->ident->vid = vstr;
3018d55b806SRobert Mustacchi 
302*499bc737SRobert Mustacchi 	if (dev != NULL) {
3038d55b806SRobert Mustacchi 		dstr = strdup(pcidb_device_name(dev));
3048d55b806SRobert Mustacchi 	} else {
3058d55b806SRobert Mustacchi 		(void) asprintf(&dstr, "pci:%x", did[0]);
3068d55b806SRobert Mustacchi 	}
3078d55b806SRobert Mustacchi 
3088d55b806SRobert Mustacchi 	if (dstr == NULL) {
3098d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to allocate pid "
3108d55b806SRobert Mustacchi 		    "string\n"), flashdev->drvname);
3118d55b806SRobert Mustacchi 		return (B_FALSE);
3128d55b806SRobert Mustacchi 	}
3138d55b806SRobert Mustacchi 	flashdev->ident->pid = dstr;
3148d55b806SRobert Mustacchi 
3158d55b806SRobert Mustacchi 	if (subdv != NULL) {
3168d55b806SRobert Mustacchi 		/*
3178d55b806SRobert Mustacchi 		 * Because this is optional, don't fail if we fail to duplicate
3188d55b806SRobert Mustacchi 		 * this.
3198d55b806SRobert Mustacchi 		 */
3208d55b806SRobert Mustacchi 		flashdev->addresses[UFM_ADDR_SUB] =
3218d55b806SRobert Mustacchi 		    strdup(pcidb_subvd_name(subdv));
3228d55b806SRobert Mustacchi 		if (flashdev->addresses[UFM_ADDR_SUB] == NULL) {
3238d55b806SRobert Mustacchi 			logmsg(MSG_WARN, gettext("%s: failed to allocate vpd "
3248d55b806SRobert Mustacchi 			    "subsystem name\n"), flashdev->drvname);
3258d55b806SRobert Mustacchi 		}
3268d55b806SRobert Mustacchi 	}
3278d55b806SRobert Mustacchi 
3288d55b806SRobert Mustacchi 	return (B_TRUE);
3298d55b806SRobert Mustacchi }
3308d55b806SRobert Mustacchi 
3318d55b806SRobert Mustacchi static int
ufmfw_di_walk_cb(di_node_t node,void * arg)3328d55b806SRobert Mustacchi ufmfw_di_walk_cb(di_node_t node, void *arg)
3338d55b806SRobert Mustacchi {
3348d55b806SRobert Mustacchi 	int ret;
3358d55b806SRobert Mustacchi 	boolean_t found = B_FALSE;
3368d55b806SRobert Mustacchi 	di_prop_t prop = DI_PROP_NIL;
3378d55b806SRobert Mustacchi 	ufmfw_ident_arg_t *uia = arg;
3388d55b806SRobert Mustacchi 	struct devicelist *flashdev = NULL;
3398d55b806SRobert Mustacchi 	ufm_ioc_getcaps_t caps;
3408d55b806SRobert Mustacchi 	ufm_ioc_bufsz_t bufsz;
3418d55b806SRobert Mustacchi 	ufm_ioc_report_t rep;
3428d55b806SRobert Mustacchi 	char *devfs, *packnvl;
3438d55b806SRobert Mustacchi 	nvlist_t *nvl = NULL;
3448d55b806SRobert Mustacchi 
3458d55b806SRobert Mustacchi 	while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) {
3468d55b806SRobert Mustacchi 		const char *pname = di_prop_name(prop);
3478d55b806SRobert Mustacchi 		if (strcmp(pname, "ddi-ufm-capable") == 0) {
3488d55b806SRobert Mustacchi 			found = B_TRUE;
3498d55b806SRobert Mustacchi 			break;
3508d55b806SRobert Mustacchi 		}
3518d55b806SRobert Mustacchi 	}
3528d55b806SRobert Mustacchi 
3538d55b806SRobert Mustacchi 	if (!found) {
3548d55b806SRobert Mustacchi 		return (DI_WALK_CONTINUE);
3558d55b806SRobert Mustacchi 	}
3568d55b806SRobert Mustacchi 
3578d55b806SRobert Mustacchi 	if (!ufmfw_node_pci(node)) {
3588d55b806SRobert Mustacchi 		return (DI_WALK_CONTINUE);
3598d55b806SRobert Mustacchi 	}
3608d55b806SRobert Mustacchi 
3618d55b806SRobert Mustacchi 	if ((devfs = di_devfs_path(node)) == NULL) {
3628d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to get device node "
3638d55b806SRobert Mustacchi 		    "path\n"), drivername);
3648d55b806SRobert Mustacchi 		goto err;
3658d55b806SRobert Mustacchi 	}
3668d55b806SRobert Mustacchi 
3678d55b806SRobert Mustacchi 	bzero(&caps, sizeof (caps));
3688d55b806SRobert Mustacchi 	caps.ufmg_version = DDI_UFM_CURRENT_VERSION;
3698d55b806SRobert Mustacchi 	(void) strlcpy(caps.ufmg_devpath, devfs, sizeof (caps.ufmg_devpath));
3708d55b806SRobert Mustacchi 	if (ioctl(ufmfw_ufm_fd, UFM_IOC_GETCAPS, &caps) != 0) {
3718d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to get UFM caps for "
3728d55b806SRobert Mustacchi 		    "UFM compatible device %s: %s\n"), drivername, devfs,
3738d55b806SRobert Mustacchi 		    strerror(errno));
3748d55b806SRobert Mustacchi 		di_devfs_path_free(devfs);
3758d55b806SRobert Mustacchi 		return (DI_WALK_CONTINUE);
3768d55b806SRobert Mustacchi 	}
3778d55b806SRobert Mustacchi 
3788d55b806SRobert Mustacchi 	/*
3798d55b806SRobert Mustacchi 	 * If nothing is supported just leave it be.
3808d55b806SRobert Mustacchi 	 */
3818d55b806SRobert Mustacchi 	if (caps.ufmg_caps == 0) {
3828d55b806SRobert Mustacchi 		di_devfs_path_free(devfs);
3838d55b806SRobert Mustacchi 		return (DI_WALK_CONTINUE);
3848d55b806SRobert Mustacchi 	}
3858d55b806SRobert Mustacchi 
3868d55b806SRobert Mustacchi 	bzero(&bufsz, sizeof (bufsz));
3878d55b806SRobert Mustacchi 	bufsz.ufbz_version = DDI_UFM_CURRENT_VERSION;
3888d55b806SRobert Mustacchi 	(void) strlcpy(bufsz.ufbz_devpath, devfs, sizeof (bufsz.ufbz_devpath));
3898d55b806SRobert Mustacchi 	if (ioctl(ufmfw_ufm_fd, UFM_IOC_REPORTSZ, &bufsz) != 0) {
3908d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to get UFM report size "
3918d55b806SRobert Mustacchi 		    "for device %s: %s\n"), drivername, devfs,
3928d55b806SRobert Mustacchi 		    strerror(errno));
3938d55b806SRobert Mustacchi 		di_devfs_path_free(devfs);
3948d55b806SRobert Mustacchi 		return (DI_WALK_CONTINUE);
3958d55b806SRobert Mustacchi 	}
3968d55b806SRobert Mustacchi 
3978d55b806SRobert Mustacchi 	if ((packnvl = malloc(bufsz.ufbz_size)) == NULL) {
3988d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to allocate %zu bytes "
3998d55b806SRobert Mustacchi 		    "for report buffer\n"), drivername, bufsz.ufbz_size);
4008d55b806SRobert Mustacchi 		di_devfs_path_free(devfs);
4018d55b806SRobert Mustacchi 		goto err;
4028d55b806SRobert Mustacchi 	}
4038d55b806SRobert Mustacchi 	bzero(&rep, sizeof (rep));
4048d55b806SRobert Mustacchi 	rep.ufmr_version = DDI_UFM_CURRENT_VERSION;
4058d55b806SRobert Mustacchi 	rep.ufmr_bufsz = bufsz.ufbz_size;
4068d55b806SRobert Mustacchi 	rep.ufmr_buf = packnvl;
4078d55b806SRobert Mustacchi 	(void) strlcpy(rep.ufmr_devpath, devfs, sizeof (rep.ufmr_devpath));
4088d55b806SRobert Mustacchi 	if (ioctl(ufmfw_ufm_fd, UFM_IOC_REPORT, &rep) != 0) {
4098d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to get UFM report "
4108d55b806SRobert Mustacchi 		    "for device %s: %s\n"), drivername, devfs,
4118d55b806SRobert Mustacchi 		    strerror(errno));
4128d55b806SRobert Mustacchi 		free(packnvl);
4138d55b806SRobert Mustacchi 		di_devfs_path_free(devfs);
4148d55b806SRobert Mustacchi 		return (DI_WALK_CONTINUE);
4158d55b806SRobert Mustacchi 	}
4168d55b806SRobert Mustacchi 
4178d55b806SRobert Mustacchi 	if ((ret = nvlist_unpack(packnvl, rep.ufmr_bufsz, &nvl, 0)) != 0) {
4188d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to unpack UFM report "
4198d55b806SRobert Mustacchi 		    "for device %s: %s\n"), drivername, devfs, strerror(ret));
4208d55b806SRobert Mustacchi 		free(packnvl);
4218d55b806SRobert Mustacchi 		di_devfs_path_free(devfs);
4228d55b806SRobert Mustacchi 		return (DI_WALK_CONTINUE);
4238d55b806SRobert Mustacchi 
4248d55b806SRobert Mustacchi 	}
4258d55b806SRobert Mustacchi 	free(packnvl);
4268d55b806SRobert Mustacchi 
4278d55b806SRobert Mustacchi 	if ((flashdev = calloc(1, sizeof (*flashdev))) == NULL) {
4288d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to allocate new "
4298d55b806SRobert Mustacchi 		    "device entry for node %s\n"), drivername, devfs);
4308d55b806SRobert Mustacchi 		di_devfs_path_free(devfs);
4318d55b806SRobert Mustacchi 		goto err;
4328d55b806SRobert Mustacchi 	}
4338d55b806SRobert Mustacchi 
4348d55b806SRobert Mustacchi 	flashdev->addresses[UFM_ADDR_PATH] = devfs;
4358d55b806SRobert Mustacchi 
4368d55b806SRobert Mustacchi 	if (asprintf(&flashdev->access_devname, "/devices%s",
4378d55b806SRobert Mustacchi 	    flashdev->addresses[UFM_ADDR_PATH]) == -1) {
4388d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to construct device "
4398d55b806SRobert Mustacchi 		    "path\n"), drivername);
4408d55b806SRobert Mustacchi 		goto err;
4418d55b806SRobert Mustacchi 	}
4428d55b806SRobert Mustacchi 	if ((flashdev->drvname = strdup(drivername)) == NULL) {
4438d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to construct driver "
4448d55b806SRobert Mustacchi 		    "name\n"), drivername);
4458d55b806SRobert Mustacchi 		goto err;
4468d55b806SRobert Mustacchi 	}
4478d55b806SRobert Mustacchi 	if ((flashdev->classname = strdup(drivername)) == NULL) {
4488d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to allocate vpd "
4498d55b806SRobert Mustacchi 		    "data\n"), drivername);
4508d55b806SRobert Mustacchi 		goto err;
4518d55b806SRobert Mustacchi 	}
4528d55b806SRobert Mustacchi 
4538d55b806SRobert Mustacchi 	if ((flashdev->ident = calloc(1, sizeof (struct vpr))) == NULL) {
4548d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to construct class "
4558d55b806SRobert Mustacchi 		    "name\n"), drivername);
4568d55b806SRobert Mustacchi 		goto err;
4578d55b806SRobert Mustacchi 	}
4588d55b806SRobert Mustacchi 	if (!ufmfw_fill_vpd(flashdev, node)) {
4598d55b806SRobert Mustacchi 		goto err;
4608d55b806SRobert Mustacchi 	}
4618d55b806SRobert Mustacchi 
4628d55b806SRobert Mustacchi 	flashdev->ident->encap_ident = nvl;
4638d55b806SRobert Mustacchi 
4648d55b806SRobert Mustacchi 	flashdev->index = uia->uia_index;
4658d55b806SRobert Mustacchi 	uia->uia_index++;
4668d55b806SRobert Mustacchi 	flashdev->addresses[UFM_ADDR_CAP] = (void *)(uintptr_t)caps.ufmg_caps;
4678d55b806SRobert Mustacchi 	flashdev->plugin = self;
4688d55b806SRobert Mustacchi 	uia->uia_nfound++;
4698d55b806SRobert Mustacchi 
4708d55b806SRobert Mustacchi 	TAILQ_INSERT_TAIL(fw_devices, flashdev, nextdev);
4718d55b806SRobert Mustacchi 
4728d55b806SRobert Mustacchi 	return (DI_WALK_CONTINUE);
4738d55b806SRobert Mustacchi 
4748d55b806SRobert Mustacchi err:
4758d55b806SRobert Mustacchi 	nvlist_free(nvl);
4768d55b806SRobert Mustacchi 	uia->uia_err = FWFLASH_FAILURE;
4778d55b806SRobert Mustacchi 	ufmfw_flashdev_free(flashdev);
4788d55b806SRobert Mustacchi 	return (DI_WALK_TERMINATE);
4798d55b806SRobert Mustacchi }
4808d55b806SRobert Mustacchi 
4818d55b806SRobert Mustacchi int
fw_identify(int start)4828d55b806SRobert Mustacchi fw_identify(int start)
4838d55b806SRobert Mustacchi {
4848d55b806SRobert Mustacchi 	ufmfw_ident_arg_t uia;
4858d55b806SRobert Mustacchi 
4868d55b806SRobert Mustacchi 	if (!ufmfw_ready) {
4878d55b806SRobert Mustacchi 		return (FWFLASH_FAILURE);
4888d55b806SRobert Mustacchi 	}
4898d55b806SRobert Mustacchi 
4908d55b806SRobert Mustacchi 	uia.uia_nfound = 0;
4918d55b806SRobert Mustacchi 	uia.uia_index = start;
4928d55b806SRobert Mustacchi 	uia.uia_err = FWFLASH_SUCCESS;
4938d55b806SRobert Mustacchi 	(void) di_walk_node(rootnode, DI_WALK_CLDFIRST, &uia,
4948d55b806SRobert Mustacchi 	    ufmfw_di_walk_cb);
4958d55b806SRobert Mustacchi 	if (uia.uia_nfound == 0) {
4968d55b806SRobert Mustacchi 		return (FWFLASH_FAILURE);
4978d55b806SRobert Mustacchi 	}
4988d55b806SRobert Mustacchi 
4998d55b806SRobert Mustacchi 	return (uia.uia_err);
5008d55b806SRobert Mustacchi }
5018d55b806SRobert Mustacchi 
5028d55b806SRobert Mustacchi int
fw_devinfo(struct devicelist * flashdev)5038d55b806SRobert Mustacchi fw_devinfo(struct devicelist *flashdev)
5048d55b806SRobert Mustacchi {
5058d55b806SRobert Mustacchi 	nvlist_t *nvl, **images;
5068d55b806SRobert Mustacchi 	uint_t nimages, img, caps;
5078d55b806SRobert Mustacchi 
5088d55b806SRobert Mustacchi 	(void) printf(gettext("Device[%d] %s\n"), flashdev->index,
5098d55b806SRobert Mustacchi 	    flashdev->access_devname);
5108d55b806SRobert Mustacchi 	(void) printf(gettext("Class [%s]\n"), flashdev->classname);
5118d55b806SRobert Mustacchi 	(void) printf(gettext("\tVendor: %s\n\tDevice: %s\n"),
5128d55b806SRobert Mustacchi 	    flashdev->ident->vid, flashdev->ident->pid);
5138d55b806SRobert Mustacchi 	if (flashdev->addresses[UFM_ADDR_SUB] != NULL) {
5148d55b806SRobert Mustacchi 		(void) printf(gettext("\tSubsystem: %s\n"),
5158d55b806SRobert Mustacchi 		    flashdev->addresses[UFM_ADDR_SUB]);
5168d55b806SRobert Mustacchi 	}
5178d55b806SRobert Mustacchi 
5188d55b806SRobert Mustacchi 	caps = (uintptr_t)flashdev->addresses[UFM_ADDR_CAP];
5198d55b806SRobert Mustacchi 	if (caps != 0) {
5208d55b806SRobert Mustacchi 		boolean_t first = B_TRUE;
5218d55b806SRobert Mustacchi 		(void) printf(gettext("\tCapabilities: "));
5228d55b806SRobert Mustacchi 		if (caps & DDI_UFM_CAP_REPORT) {
5238d55b806SRobert Mustacchi 			(void) printf(gettext("Report"));
5248d55b806SRobert Mustacchi 			first = B_FALSE;
5258d55b806SRobert Mustacchi 		}
5268d55b806SRobert Mustacchi 
5278d55b806SRobert Mustacchi 		if (caps & DDI_UFM_CAP_READIMG) {
5288d55b806SRobert Mustacchi 			(void) printf(gettext("%sRead Image"),
5298d55b806SRobert Mustacchi 			    first ? "" : ", ");
5308d55b806SRobert Mustacchi 		}
5318d55b806SRobert Mustacchi 		(void) printf("\n");
5328d55b806SRobert Mustacchi 	}
5338d55b806SRobert Mustacchi 
5348d55b806SRobert Mustacchi 	nvl = flashdev->ident->encap_ident;
5358d55b806SRobert Mustacchi 	if (nvlist_lookup_nvlist_array(nvl, DDI_UFM_NV_IMAGES, &images,
5368d55b806SRobert Mustacchi 	    &nimages) != 0) {
5378d55b806SRobert Mustacchi 		goto done;
5388d55b806SRobert Mustacchi 	}
5398d55b806SRobert Mustacchi 
5408d55b806SRobert Mustacchi 	for (img = 0; img < nimages; img++) {
5418d55b806SRobert Mustacchi 		nvlist_t **slots;
5428d55b806SRobert Mustacchi 		uint_t nslots, s;
5438d55b806SRobert Mustacchi 		char *desc;
5448d55b806SRobert Mustacchi 
5458d55b806SRobert Mustacchi 		if (nvlist_lookup_nvlist_array(images[img],
5468d55b806SRobert Mustacchi 		    DDI_UFM_NV_IMAGE_SLOTS, &slots, &nslots) != 0) {
5478d55b806SRobert Mustacchi 			goto done;
5488d55b806SRobert Mustacchi 		}
5498d55b806SRobert Mustacchi 
5508d55b806SRobert Mustacchi 		if (nvlist_lookup_string(images[img], DDI_UFM_NV_IMAGE_DESC,
5518d55b806SRobert Mustacchi 		    &desc) != 0) {
5528d55b806SRobert Mustacchi 			desc = NULL;
5538d55b806SRobert Mustacchi 		}
5548d55b806SRobert Mustacchi 
5558d55b806SRobert Mustacchi 		if (desc != NULL) {
5568d55b806SRobert Mustacchi 			(void) printf(gettext("\tImage %d: %s\n"), img, desc);
5578d55b806SRobert Mustacchi 		} else {
5588d55b806SRobert Mustacchi 			(void) printf(gettext("\tImage %d:\n"), img);
5598d55b806SRobert Mustacchi 		}
5608d55b806SRobert Mustacchi 
5618d55b806SRobert Mustacchi 		for (s = 0; s < nslots; s++) {
5628d55b806SRobert Mustacchi 			uint32_t attr;
5638d55b806SRobert Mustacchi 			char *version;
5648d55b806SRobert Mustacchi 
5658d55b806SRobert Mustacchi 			if (nvlist_lookup_uint32(slots[s], DDI_UFM_NV_SLOT_ATTR,
5668d55b806SRobert Mustacchi 			    &attr) != 0) {
5678d55b806SRobert Mustacchi 				attr = 0;
5688d55b806SRobert Mustacchi 			}
5698d55b806SRobert Mustacchi 
5708d55b806SRobert Mustacchi 			if (nvlist_lookup_string(slots[s],
5718d55b806SRobert Mustacchi 			    DDI_UFM_NV_SLOT_VERSION, &version) != 0) {
5728d55b806SRobert Mustacchi 				version = "<unknown>";
5738d55b806SRobert Mustacchi 			}
5748d55b806SRobert Mustacchi 
5758d55b806SRobert Mustacchi 			printf(gettext("\t    Slot %d (%c|%c|%c): %s\n"), s,
5768d55b806SRobert Mustacchi 			    attr & DDI_UFM_ATTR_READABLE ? 'r' : '-',
5778d55b806SRobert Mustacchi 			    attr & DDI_UFM_ATTR_WRITEABLE ? 'w' : '-',
5788d55b806SRobert Mustacchi 			    attr & DDI_UFM_ATTR_ACTIVE ? 'a' : '-',
5798d55b806SRobert Mustacchi 			    attr & DDI_UFM_ATTR_EMPTY ? "<empty>" : version);
5808d55b806SRobert Mustacchi 
5818d55b806SRobert Mustacchi 		}
5828d55b806SRobert Mustacchi 	}
5838d55b806SRobert Mustacchi 
5848d55b806SRobert Mustacchi done:
5858d55b806SRobert Mustacchi 	(void) printf("\n\n");
5868d55b806SRobert Mustacchi 	return (FWFLASH_SUCCESS);
5878d55b806SRobert Mustacchi }
5888d55b806SRobert Mustacchi 
5898d55b806SRobert Mustacchi void
fw_cleanup(struct devicelist * flashdev)5908d55b806SRobert Mustacchi fw_cleanup(struct devicelist *flashdev)
5918d55b806SRobert Mustacchi {
5928d55b806SRobert Mustacchi 	ufmfw_flashdev_free(flashdev);
5938d55b806SRobert Mustacchi }
5948d55b806SRobert Mustacchi 
5958d55b806SRobert Mustacchi #pragma init(ufmfw_init)
5968d55b806SRobert Mustacchi static void
ufmfw_init(void)5978d55b806SRobert Mustacchi ufmfw_init(void)
5988d55b806SRobert Mustacchi {
5998d55b806SRobert Mustacchi 	ufmfw_ufm_fd = open("/dev/ufm", O_RDONLY);
6008d55b806SRobert Mustacchi 	if (ufmfw_ufm_fd < 0) {
6018d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to open /dev/ufm: %s\n"),
6028d55b806SRobert Mustacchi 		    drivername, strerror(errno));
6038d55b806SRobert Mustacchi 		return;
6048d55b806SRobert Mustacchi 	}
6058d55b806SRobert Mustacchi 
6068d55b806SRobert Mustacchi 	ufmfw_pcidb = pcidb_open(PCIDB_VERSION);
6078d55b806SRobert Mustacchi 	if (ufmfw_pcidb == NULL) {
6088d55b806SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to open libpcidb: %s\n"),
6098d55b806SRobert Mustacchi 		    drivername, strerror(errno));
6108d55b806SRobert Mustacchi 		return;
6118d55b806SRobert Mustacchi 	}
6128d55b806SRobert Mustacchi 	ufmfw_ready = B_TRUE;
6138d55b806SRobert Mustacchi }
6148d55b806SRobert Mustacchi 
6158d55b806SRobert Mustacchi #pragma fini(ufmfw_fini)
6168d55b806SRobert Mustacchi static void
ufmfw_fini(void)6178d55b806SRobert Mustacchi ufmfw_fini(void)
6188d55b806SRobert Mustacchi {
6198d55b806SRobert Mustacchi 	pcidb_close(ufmfw_pcidb);
6208d55b806SRobert Mustacchi 	if (ufmfw_ufm_fd >= 0) {
6218d55b806SRobert Mustacchi 		(void) close(ufmfw_ufm_fd);
6228d55b806SRobert Mustacchi 	}
6238d55b806SRobert Mustacchi 	ufmfw_ready = B_FALSE;
6248d55b806SRobert Mustacchi }