xref: /illumos-gate/usr/src/lib/libipmi/common/ipmi_sdr.c (revision 02bc52be7430b2f7fafe1a2c981bff49ef11d6fa)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <libipmi.h>
30 #include <stddef.h>
31 #include <string.h>
32 
33 #include "ipmi_impl.h"
34 
35 typedef struct ipmi_cmd_get_sdr {
36 	uint16_t	ic_gs_resid;
37 	uint16_t	ic_gs_recid;
38 	uint8_t		ic_gs_offset;
39 	uint8_t		ic_gs_len;
40 } ipmi_cmd_get_sdr_t;
41 
42 typedef struct ipmi_rsp_get_sdr {
43 	uint16_t	ir_gs_next;
44 	uint8_t		ir_gs_record[1];
45 } ipmi_rsp_get_sdr_t;
46 
47 /*
48  * Issue the "Reserve SDR Repository" command.
49  */
50 static int
51 ipmi_sdr_reserve_repository(ipmi_handle_t *ihp)
52 {
53 	ipmi_cmd_t cmd, *rsp;
54 
55 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
56 	cmd.ic_lun = 0;
57 	cmd.ic_cmd = IPMI_CMD_RESERVE_SDR_REPOSITORY;
58 	cmd.ic_dlen = 0;
59 	cmd.ic_data = NULL;
60 
61 	if ((rsp = ipmi_send(ihp, &cmd)) == NULL)
62 		return (-1);
63 
64 	ihp->ih_reservation = *((uint16_t *)rsp->ic_data);
65 	return (0);
66 }
67 
68 /*
69  * Refresh the cache of sensor data records.
70  */
71 static int
72 ipmi_sdr_refresh(ipmi_handle_t *ihp)
73 {
74 	size_t len;
75 	uint16_t id;
76 	ipmi_sdr_t *sdr;
77 	ipmi_sdr_cache_ent_t *ent;
78 	ipmi_sdr_generic_locator_t *gen_src, *gen_dst;
79 	ipmi_sdr_fru_locator_t *fru_src, *fru_dst;
80 
81 	ipmi_sdr_clear(ihp);
82 
83 	/*
84 	 * Iterate over all existing SDRs and add them to the cache.
85 	 */
86 	id = IPMI_SDR_FIRST;
87 	while (id != IPMI_SDR_LAST) {
88 		if ((sdr = ipmi_sdr_get(ihp, id, &id)) == NULL)
89 			return (-1);
90 
91 		/*
92 		 * We currently only understand FRU and generic device records.
93 		 */
94 		if (sdr->is_type != IPMI_SDR_TYPE_GENERIC_LOCATOR &&
95 		    sdr->is_type != IPMI_SDR_TYPE_FRU_LOCATOR)
96 			continue;
97 
98 		/*
99 		 * Create a copy of the SDR-specific data.
100 		 */
101 		gen_dst = NULL;
102 		fru_dst = NULL;
103 		switch (sdr->is_type) {
104 		case IPMI_SDR_TYPE_GENERIC_LOCATOR:
105 			gen_src = (ipmi_sdr_generic_locator_t *)sdr->is_record;
106 			len = offsetof(ipmi_sdr_generic_locator_t,
107 			    is_gl_idstring) + gen_src->is_gl_idlen + 1;
108 			if ((gen_dst = ipmi_alloc(ihp, len)) == NULL)
109 				return (-1);
110 			(void) memcpy(gen_dst, gen_src, len - 1);
111 			((char *)gen_dst)[len - 1] = '\0';
112 			break;
113 
114 		case IPMI_SDR_TYPE_FRU_LOCATOR:
115 			fru_src = (ipmi_sdr_fru_locator_t *)sdr->is_record;
116 			len = offsetof(ipmi_sdr_fru_locator_t,
117 			    is_fl_idstring) + fru_src->is_fl_idlen + 1;
118 			if ((fru_dst = ipmi_alloc(ihp, len)) == NULL)
119 				return (-1);
120 			(void) memcpy(fru_dst, fru_src, len - 1);
121 			((char *)fru_dst)[len - 1] = '\0';
122 			break;
123 		}
124 
125 		if ((ent = ipmi_alloc(ihp,
126 		    sizeof (ipmi_sdr_cache_ent_t))) == NULL) {
127 			ipmi_free(ihp, gen_dst);
128 			ipmi_free(ihp, fru_dst);
129 			return (-1);
130 		}
131 
132 		ent->isc_generic = gen_dst;
133 		ent->isc_fru = fru_dst;
134 		ent->isc_next = ihp->ih_sdr_cache;
135 		ent->isc_type = sdr->is_type;
136 		ihp->ih_sdr_cache = ent;
137 	}
138 
139 	return (0);
140 }
141 
142 void
143 ipmi_sdr_clear(ipmi_handle_t *ihp)
144 {
145 	ipmi_sdr_cache_ent_t *ent, *next;
146 
147 	while ((ent = ihp->ih_sdr_cache) != NULL) {
148 		next = ent->isc_next;
149 		ipmi_free(ihp, ent->isc_generic);
150 		ipmi_free(ihp, ent->isc_fru);
151 		ipmi_free(ihp, ent);
152 		ihp->ih_sdr_cache = next;
153 	}
154 }
155 
156 ipmi_sdr_t *
157 ipmi_sdr_get(ipmi_handle_t *ihp, uint16_t id, uint16_t *next)
158 {
159 	ipmi_cmd_t cmd, *rsp;
160 	ipmi_cmd_get_sdr_t req;
161 	ipmi_rsp_get_sdr_t *sdr;
162 	int i;
163 
164 	req.ic_gs_resid = ihp->ih_reservation;
165 	req.ic_gs_recid = id;
166 	req.ic_gs_offset = 0;
167 	req.ic_gs_len = 0xFF;
168 
169 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
170 	cmd.ic_lun = 0;
171 	cmd.ic_cmd = IPMI_CMD_GET_SDR;
172 	cmd.ic_dlen = sizeof (req);
173 	cmd.ic_data = &req;
174 
175 	for (i = 0; i < ihp->ih_retries; i++) {
176 		if ((rsp = ipmi_send(ihp, &cmd)) == NULL) {
177 			if (ipmi_errno(ihp) != EIPMI_INVALID_RESERVATION)
178 				return (NULL);
179 
180 			if (ipmi_sdr_reserve_repository(ihp) != 0)
181 				return (NULL);
182 			req.ic_gs_resid = ihp->ih_reservation;
183 		}
184 	}
185 
186 	if (rsp == NULL)
187 		return (NULL);
188 
189 	if (rsp->ic_dlen < sizeof (uint16_t) + sizeof (ipmi_sdr_t)) {
190 		(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
191 		return (NULL);
192 	}
193 
194 	sdr = rsp->ic_data;
195 	*next = sdr->ir_gs_next;
196 
197 	return ((ipmi_sdr_t *)sdr->ir_gs_record);
198 }
199 
200 ipmi_sdr_fru_locator_t *
201 ipmi_sdr_lookup_fru(ipmi_handle_t *ihp, const char *idstr)
202 {
203 	ipmi_sdr_cache_ent_t *ent;
204 
205 	if (ihp->ih_sdr_cache == NULL &&
206 	    ipmi_sdr_refresh(ihp) != 0)
207 		return (NULL);
208 
209 	for (ent = ihp->ih_sdr_cache; ent != NULL; ent = ent->isc_next) {
210 		if (ent->isc_type != IPMI_SDR_TYPE_FRU_LOCATOR)
211 			continue;
212 
213 		if (strcmp(ent->isc_fru->is_fl_idstring, idstr) == 0)
214 			return (ent->isc_fru);
215 	}
216 
217 	(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
218 	return (NULL);
219 }
220 
221 ipmi_sdr_generic_locator_t *
222 ipmi_sdr_lookup_generic(ipmi_handle_t *ihp, const char *idstr)
223 {
224 	ipmi_sdr_cache_ent_t *ent;
225 
226 	if (ihp->ih_sdr_cache == NULL &&
227 	    ipmi_sdr_refresh(ihp) != 0)
228 		return (NULL);
229 
230 	for (ent = ihp->ih_sdr_cache; ent != NULL; ent = ent->isc_next) {
231 		if (ent->isc_type != IPMI_SDR_TYPE_GENERIC_LOCATOR)
232 			continue;
233 
234 		if (strcmp(ent->isc_generic->is_gl_idstring, idstr) == 0)
235 			return (ent->isc_generic);
236 	}
237 
238 	(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
239 	return (NULL);
240 }
241