1 /*
2  * solparm.c
3  *
4  * MontaVista IPMI code for configuring SoL data
5  *
6  * Author: MontaVista Software, Inc.
7  *         Corey Minyard <minyard@mvista.com>
8  *         source@mvista.com
9  *
10  * Copyright 2006 MontaVista Software Inc.
11  *
12  *  This program is free software; you can redistribute it and/or
13  *  modify it under the terms of the GNU Lesser General Public License
14  *  as published by the Free Software Foundation; either version 2 of
15  *  the License, or (at your option) any later version.
16  *
17  *
18  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
19  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
27  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  You should have received a copy of the GNU Lesser General Public
30  *  License along with this program; if not, write to the Free
31  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32  */
33 
34 #include <string.h>
35 #include <math.h>
36 #include <stdio.h>
37 
38 #include <OpenIPMI/ipmiif.h>
39 #include <OpenIPMI/ipmi_solparm.h>
40 #include <OpenIPMI/ipmi_msgbits.h>
41 #include <OpenIPMI/ipmi_err.h>
42 
43 #include <OpenIPMI/internal/opq.h>
44 #include <OpenIPMI/internal/locked_list.h>
45 #include <OpenIPMI/internal/ipmi_domain.h>
46 #include <OpenIPMI/internal/ipmi_mc.h>
47 #include <OpenIPMI/internal/ipmi_int.h>
48 
49 #define IPMI_SOLPARM_ATTR_NAME "ipmi_solparm"
50 
51 struct ipmi_solparm_s
52 {
53     ipmi_mcid_t      mc;
54     ipmi_domain_id_t domain;
55     unsigned char    channel;
56 
57     int refcount;
58 
59     char name[IPMI_SOLPARM_NAME_LEN];
60 
61     unsigned int destroyed : 1;
62     unsigned int in_destroy : 1;
63     unsigned int locked : 1;
64     unsigned int in_list : 1;
65 
66     /* Something to call when the destroy is complete. */
67     ipmi_solparm_done_cb destroy_handler;
68     void                 *destroy_cb_data;
69 
70     os_hnd_lock_t *solparm_lock;
71 
72     os_handler_t *os_hnd;
73 
74     /* We serialize operations through here, since we are dealing with
75        a locked resource. */
76     opq_t *opq;
77 };
78 
79 static int
solparm_attr_init(ipmi_domain_t * domain,void * cb_data,void ** data)80 solparm_attr_init(ipmi_domain_t *domain, void *cb_data, void **data)
81 {
82     locked_list_t *solparml;
83 
84     solparml = locked_list_alloc(ipmi_domain_get_os_hnd(domain));
85     if (!solparml)
86 	return ENOMEM;
87 
88     *data = solparml;
89     return 0;
90 }
91 
92 static void
solparm_lock(ipmi_solparm_t * solparm)93 solparm_lock(ipmi_solparm_t *solparm)
94 {
95     if (solparm->os_hnd->lock)
96 	solparm->os_hnd->lock(solparm->os_hnd, solparm->solparm_lock);
97 }
98 
99 static void
solparm_unlock(ipmi_solparm_t * solparm)100 solparm_unlock(ipmi_solparm_t *solparm)
101 {
102     if (solparm->os_hnd->lock)
103 	solparm->os_hnd->unlock(solparm->os_hnd, solparm->solparm_lock);
104 }
105 
106 static void
solparm_get(ipmi_solparm_t * solparm)107 solparm_get(ipmi_solparm_t *solparm)
108 {
109     solparm_lock(solparm);
110     solparm->refcount++;
111     solparm_unlock(solparm);
112 }
113 
114 static void internal_destroy_solparm(ipmi_solparm_t *solparm);
115 
116 static void
solparm_put(ipmi_solparm_t * solparm)117 solparm_put(ipmi_solparm_t *solparm)
118 {
119     solparm_lock(solparm);
120     solparm->refcount--;
121     if (solparm->refcount == 0) {
122 	internal_destroy_solparm(solparm);
123 	return;
124     }
125     solparm_unlock(solparm);
126 }
127 
128 void
ipmi_solparm_ref(ipmi_solparm_t * solparm)129 ipmi_solparm_ref(ipmi_solparm_t *solparm)
130 {
131     solparm_get(solparm);
132 }
133 
134 void
ipmi_solparm_deref(ipmi_solparm_t * solparm)135 ipmi_solparm_deref(ipmi_solparm_t *solparm)
136 {
137     solparm_put(solparm);
138 }
139 
140 static int
destroy_solparm(void * cb_data,void * item1,void * item2)141 destroy_solparm(void *cb_data, void *item1, void *item2)
142 {
143     ipmi_solparm_t *solparm = item1;
144 
145     solparm_lock(solparm);
146     solparm->in_list = 1;
147     solparm_unlock(solparm);
148     return LOCKED_LIST_ITER_CONTINUE;
149 }
150 
151 static void
solparm_attr_destroy(void * cb_data,void * data)152 solparm_attr_destroy(void *cb_data, void *data)
153 {
154     locked_list_t *solparml = data;
155 
156     locked_list_iterate(solparml, destroy_solparm, NULL);
157     locked_list_destroy(solparml);
158 }
159 
160 typedef struct iterate_solparms_info_s
161 {
162     ipmi_solparm_ptr_cb handler;
163     void                *cb_data;
164 } iterate_solparms_info_t;
165 
166 static int
solparms_handler(void * cb_data,void * item1,void * item2)167 solparms_handler(void *cb_data, void *item1, void *item2)
168 {
169     iterate_solparms_info_t *info = cb_data;
170     info->handler(item1, info->cb_data);
171     solparm_put(item1);
172     return LOCKED_LIST_ITER_CONTINUE;
173 }
174 
175 static int
solparms_prefunc(void * cb_data,void * item1,void * item2)176 solparms_prefunc(void *cb_data, void *item1, void *item2)
177 {
178     ipmi_solparm_t *solparm = item1;
179     solparm_get(solparm);
180     return LOCKED_LIST_ITER_CONTINUE;
181 }
182 
183 void
ipmi_solparm_iterate_solparms(ipmi_domain_t * domain,ipmi_solparm_ptr_cb handler,void * cb_data)184 ipmi_solparm_iterate_solparms(ipmi_domain_t       *domain,
185 			      ipmi_solparm_ptr_cb handler,
186 			      void                *cb_data)
187 {
188     iterate_solparms_info_t info;
189     ipmi_domain_attr_t      *attr;
190     locked_list_t           *solparms;
191     int                     rv;
192 
193     rv = ipmi_domain_find_attribute(domain, IPMI_SOLPARM_ATTR_NAME,
194 				    &attr);
195     if (rv)
196 	return;
197     solparms = ipmi_domain_attr_get_data(attr);
198 
199     info.handler = handler;
200     info.cb_data = cb_data;
201     locked_list_iterate_prefunc(solparms, solparms_prefunc,
202 				solparms_handler, &info);
203     ipmi_domain_attr_put(attr);
204 }
205 
206 ipmi_mcid_t
ipmi_solparm_get_mc_id(ipmi_solparm_t * solparm)207 ipmi_solparm_get_mc_id(ipmi_solparm_t *solparm)
208 {
209     return solparm->mc;
210 }
211 
212 unsigned int
ipmi_solparm_get_channel(ipmi_solparm_t * solparm)213 ipmi_solparm_get_channel(ipmi_solparm_t *solparm)
214 {
215     return solparm->channel;
216 }
217 
218 int
ipmi_solparm_get_name(ipmi_solparm_t * solparm,char * name,int length)219 ipmi_solparm_get_name(ipmi_solparm_t *solparm, char *name, int length)
220 {
221     int  slen;
222 
223     if (length <= 0)
224 	return 0;
225 
226     /* Never changes, no lock needed. */
227     slen = strlen(solparm->name);
228     if (slen == 0) {
229 	if (name)
230 	    *name = '\0';
231 	goto out;
232     }
233 
234     if (name) {
235 	memcpy(name, solparm->name, slen);
236 	name[slen] = '\0';
237     }
238  out:
239     return slen;
240 }
241 
242 static int
check_solparm_response_param(ipmi_solparm_t * solparm,ipmi_mc_t * mc,ipmi_msg_t * rsp,int len,char * func_name)243 check_solparm_response_param(ipmi_solparm_t *solparm,
244 			     ipmi_mc_t      *mc,
245 			     ipmi_msg_t     *rsp,
246 			     int	    len,
247 			     char	    *func_name)
248 {
249     if (solparm->destroyed) {
250 	ipmi_log(IPMI_LOG_ERR_INFO,
251 		 "%ssolparm.c(%s): "
252 		 "SOLPARM was destroyed while an operation was in progress",
253 		 MC_NAME(mc), func_name);
254 	return ECANCELED;
255     }
256 
257     if (!mc) {
258 	ipmi_log(IPMI_LOG_ERR_INFO,
259 		 "%ssolparm.c(%s): "
260 		 "MC went away while SOLPARM op was in progress",
261 		 MC_NAME(mc), func_name);
262 	return ECANCELED;
263     }
264 
265     if (rsp->data[0] != 0) {
266 #if 0
267 	/* Sometimes this comes in and is valid (like when writing
268 	   parm 0 to value 2), just ignore it. */
269 	/* We ignore 0x80, since that may be a valid error return for an
270 	   unsupported parameter.  We also ignore 0x82, just to avoid
271 	   extraneous errors. */
272 	if ((rsp->data[0] != 0x80) && (rsp->data[0] != 0x82))
273 	    ipmi_log(IPMI_LOG_ERR_INFO,
274 		     "%ssolparm.c(%s): "
275 		     "IPMI error from SOLPARM capabilities fetch: %x",
276 		     MC_NAME(mc), func_name, rsp->data[0]);
277 #endif
278 	return IPMI_IPMI_ERR_VAL(rsp->data[0]);
279     }
280 
281     if (rsp->data_len < len) {
282 	ipmi_log(IPMI_LOG_ERR_INFO,
283 		"%ssolparm.c(%s): SOLPARM capabilities too short",
284 		 MC_NAME(mc), func_name);
285 	return EINVAL;
286     }
287     return 0;
288 }
289 
290 int
ipmi_solparm_alloc(ipmi_mc_t * mc,unsigned int channel,ipmi_solparm_t ** new_solparm)291 ipmi_solparm_alloc(ipmi_mc_t      *mc,
292 		   unsigned int   channel,
293 		   ipmi_solparm_t **new_solparm)
294 {
295     ipmi_solparm_t     *solparm = NULL;
296     int                rv = 0;
297     ipmi_domain_t      *domain = ipmi_mc_get_domain(mc);
298     int                p, len;
299     locked_list_t      *solparml;
300     ipmi_domain_attr_t *attr;
301 
302     CHECK_MC_LOCK(mc);
303 
304     rv = ipmi_domain_register_attribute(domain, IPMI_SOLPARM_ATTR_NAME,
305 					solparm_attr_init,
306 					solparm_attr_destroy,
307 					NULL,
308 					&attr);
309     if (rv)
310 	return rv;
311     solparml = ipmi_domain_attr_get_data(attr);
312 
313     solparm = ipmi_mem_alloc(sizeof(*solparm));
314     if (!solparm) {
315 	rv = ENOMEM;
316 	goto out;
317     }
318     memset(solparm, 0, sizeof(*solparm));
319 
320     solparm->refcount = 1;
321     solparm->in_list = 1;
322     solparm->mc = ipmi_mc_convert_to_id(mc);
323     solparm->domain = ipmi_domain_convert_to_id(domain);
324     len = sizeof(solparm->name);
325     p = ipmi_domain_get_name(domain, solparm->name, len);
326     len -= p;
327     snprintf(solparm->name+p, len, ".%d", ipmi_domain_get_unique_num(domain));
328     solparm->os_hnd = ipmi_domain_get_os_hnd(domain);
329     solparm->solparm_lock = NULL;
330     solparm->channel = channel & 0xf;
331 
332     solparm->opq = opq_alloc(solparm->os_hnd);
333     if (!solparm->opq) {
334 	rv = ENOMEM;
335 	goto out;
336     }
337 
338     if (solparm->os_hnd->create_lock) {
339 	rv = solparm->os_hnd->create_lock(solparm->os_hnd,
340 					  &solparm->solparm_lock);
341 	if (rv)
342 	    goto out;
343     }
344 
345     if (! locked_list_add(solparml, solparm, NULL)) {
346 	rv = ENOMEM;
347 	goto out;
348     }
349 
350  out:
351     if (rv) {
352 	if (solparm) {
353 	    if (solparm->opq)
354 		opq_destroy(solparm->opq);
355 	    if (solparm->solparm_lock)
356 		solparm->os_hnd->destroy_lock(solparm->os_hnd,
357 					      solparm->solparm_lock);
358 	    ipmi_mem_free(solparm);
359 	}
360     } else {
361 	*new_solparm = solparm;
362     }
363     ipmi_domain_attr_put(attr);
364     return rv;
365 }
366 
367 static void
internal_destroy_solparm(ipmi_solparm_t * solparm)368 internal_destroy_solparm(ipmi_solparm_t *solparm)
369 {
370     solparm->in_destroy = 1;
371 
372     /* We don't have to have a valid ipmi to destroy a solparm, they
373        are designed to live after the ipmi has been destroyed. */
374 
375     if (solparm->in_list) {
376 	int                rv;
377 	ipmi_domain_attr_t *attr;
378 	locked_list_t      *solparml;
379 
380 	rv = ipmi_domain_id_find_attribute(solparm->domain,
381 					   IPMI_SOLPARM_ATTR_NAME,
382 					   &attr);
383 	if (!rv) {
384 	    solparm->refcount++;
385 	    solparm->in_list = 0;
386 	    solparm_unlock(solparm);
387 	    solparml = ipmi_domain_attr_get_data(attr);
388 
389 	    locked_list_remove(solparml, solparm, NULL);
390 	    ipmi_domain_attr_put(attr);
391 	    solparm_lock(solparm);
392 	    /* While we were unlocked, someone may have come in and
393 	       grabbed the solparm by iterating the list of solparms.
394 	       That's ok, we just let them handle the destruction
395 	       since this code will not be entered again. */
396 	    if (solparm->refcount != 1) {
397 		solparm->refcount--;
398 		solparm_unlock(solparm);
399 		return;
400 	    }
401 	}
402     }
403     solparm_unlock(solparm);
404 
405     if (solparm->opq)
406 	opq_destroy(solparm->opq);
407 
408     if (solparm->solparm_lock)
409 	solparm->os_hnd->destroy_lock(solparm->os_hnd, solparm->solparm_lock);
410 
411     /* Do this after we have gotten rid of all external dependencies,
412        but before it is free. */
413     if (solparm->destroy_handler)
414 	solparm->destroy_handler(solparm, 0, solparm->destroy_cb_data);
415 
416     ipmi_mem_free(solparm);
417 }
418 
419 int
ipmi_solparm_destroy(ipmi_solparm_t * solparm,ipmi_solparm_done_cb done,void * cb_data)420 ipmi_solparm_destroy(ipmi_solparm_t       *solparm,
421 		     ipmi_solparm_done_cb done,
422 		     void                 *cb_data)
423 
424 {
425     solparm_lock(solparm);
426     if (solparm->in_list) {
427 	int                rv;
428 	ipmi_domain_attr_t *attr;
429 	locked_list_t      *solparml;
430 
431 	solparm->in_list = 0;
432 	rv = ipmi_domain_id_find_attribute(solparm->domain,
433 					   IPMI_SOLPARM_ATTR_NAME,
434 					   &attr);
435 	if (!rv) {
436 	    solparm_unlock(solparm);
437 	    solparml = ipmi_domain_attr_get_data(attr);
438 
439 	    locked_list_remove(solparml, solparm, NULL);
440 	    ipmi_domain_attr_put(attr);
441 	    solparm_lock(solparm);
442 	}
443     }
444 
445     if (solparm->destroyed) {
446 	solparm_unlock(solparm);
447 	return EINVAL;
448     }
449     solparm->destroyed = 1;
450     solparm_unlock(solparm);
451     solparm->destroy_handler = done;
452     solparm->destroy_cb_data = cb_data;
453 
454     solparm_put(solparm);
455     return 0;
456 }
457 
458 typedef struct solparm_fetch_handler_s
459 {
460     ipmi_solparm_t 	*solparm;
461     unsigned char       parm;
462     unsigned char       set;
463     unsigned char       block;
464     ipmi_solparm_get_cb handler;
465     void                *cb_data;
466     unsigned char       *data;
467     unsigned int        data_len;
468     int                 rv;
469 } solparm_fetch_handler_t;
470 
471 /* This should be called with the solparm locked.  It will unlock the solparm
472    before returning. */
473 static void
fetch_complete(ipmi_solparm_t * solparm,int err,solparm_fetch_handler_t * elem)474 fetch_complete(ipmi_solparm_t *solparm, int err, solparm_fetch_handler_t *elem)
475 {
476     if (solparm->in_destroy)
477 	goto out;
478 
479     solparm_unlock(solparm);
480 
481     if (elem->handler)
482 	elem->handler(solparm, err, elem->data, elem->data_len, elem->cb_data);
483 
484     ipmi_mem_free(elem);
485 
486     if (!solparm->destroyed)
487 	opq_op_done(solparm->opq);
488 
489     solparm_put(solparm);
490     return;
491 
492  out:
493     solparm_unlock(solparm);
494     solparm_put(solparm);
495 }
496 
497 
498 static void
solparm_config_fetched(ipmi_mc_t * mc,ipmi_msg_t * rsp,void * rsp_data)499 solparm_config_fetched(ipmi_mc_t  *mc,
500 		       ipmi_msg_t *rsp,
501 		       void       *rsp_data)
502 {
503     solparm_fetch_handler_t *elem = rsp_data;
504     ipmi_solparm_t          *solparm = elem->solparm;
505     int                 rv;
506 
507     rv = check_solparm_response_param(solparm, mc, rsp, 2,
508 				      "solparm_config_fetched");
509 
510     /* Skip over the completion code. */
511     elem->data = rsp->data + 1;
512     elem->data_len = rsp->data_len - 1;
513 
514     solparm_lock(solparm);
515     fetch_complete(solparm, rv, elem);
516 }
517 
518 static void
start_config_fetch_cb(ipmi_mc_t * mc,void * cb_data)519 start_config_fetch_cb(ipmi_mc_t *mc, void *cb_data)
520 {
521     solparm_fetch_handler_t *elem = cb_data;
522     ipmi_solparm_t          *solparm = elem->solparm;
523     unsigned char           data[4];
524     ipmi_msg_t              msg;
525     int                     rv;
526 
527     solparm_lock(solparm);
528     if (solparm->destroyed) {
529 	ipmi_log(IPMI_LOG_ERR_INFO,
530 		 "%ssolparm.c(start_config_fetch_cb): "
531 		 "SOLPARM was destroyed while an operation was in progress",
532 		 MC_NAME(mc));
533 	fetch_complete(solparm, ECANCELED, elem);
534 	goto out;
535     }
536 
537     msg.data = data;
538     msg.netfn = IPMI_TRANSPORT_NETFN;
539     msg.cmd = IPMI_GET_SOL_CONFIGURATION_PARAMETERS;
540     data[0] = solparm->channel;
541     data[1] = elem->parm;
542     data[2] = elem->set;
543     data[3] = elem->block;
544     msg.data_len = 4;
545     rv = ipmi_mc_send_command(mc, 0, &msg, solparm_config_fetched, elem);
546 
547     if (rv) {
548 	ipmi_log(IPMI_LOG_ERR_INFO,
549 		 "%ssolparm.c(start_config_fetch_cb): "
550 		 "SOLPARM start_config_fetch: could not send cmd: %x",
551 		 MC_NAME(mc), rv);
552 	fetch_complete(solparm, ECANCELED, elem);
553 	goto out;
554     }
555 
556     solparm_unlock(solparm);
557  out:
558     return;
559 }
560 
561 static int
start_config_fetch(void * cb_data,int shutdown)562 start_config_fetch(void *cb_data, int shutdown)
563 {
564     solparm_fetch_handler_t *elem = cb_data;
565     int                 rv;
566 
567     if (shutdown) {
568 	ipmi_log(IPMI_LOG_ERR_INFO,
569 		 "solparm.c(start_config_fetch): "
570 		 "SOLPARM was destroyed while an operation was in progress");
571 	solparm_lock(elem->solparm);
572 	fetch_complete(elem->solparm, ECANCELED, elem);
573 	return OPQ_HANDLER_STARTED;
574     }
575 
576     /* The read lock must be claimed before the solparm lock to avoid
577        deadlock. */
578     rv = ipmi_mc_pointer_cb(elem->solparm->mc, start_config_fetch_cb, elem);
579     if (rv) {
580 	ipmi_log(IPMI_LOG_ERR_INFO,
581 		 "solparm.c(start_config_fetch): "
582 		 "SOLPARM's MC is not valid");
583 	solparm_lock(elem->solparm);
584 	fetch_complete(elem->solparm, rv, elem);
585     }
586     return OPQ_HANDLER_STARTED;
587 }
588 
589 int
ipmi_solparm_get_parm(ipmi_solparm_t * solparm,unsigned int parm,unsigned int set,unsigned int block,ipmi_solparm_get_cb done,void * cb_data)590 ipmi_solparm_get_parm(ipmi_solparm_t      *solparm,
591 		      unsigned int	  parm,
592 		      unsigned int	  set,
593 		      unsigned int	  block,
594 		      ipmi_solparm_get_cb done,
595 		      void                *cb_data)
596 {
597     solparm_fetch_handler_t *elem;
598     int                 rv = 0;
599 
600     if (solparm->destroyed)
601 	return EINVAL;
602 
603     elem = ipmi_mem_alloc(sizeof(*elem));
604     if (!elem) {
605 	ipmi_log(IPMI_LOG_ERR_INFO,
606 		 "solparm.c(ipmi_solparm_get_parm): "
607 		 "could not allocate the solparm element");
608 	return ENOMEM;
609     }
610 
611     elem->handler = done;
612     elem->cb_data = cb_data;
613     elem->solparm = solparm;
614     elem->parm = parm;
615     elem->set = set;
616     elem->block = block;
617     elem->rv = 0;
618 
619     if (!opq_new_op(solparm->opq, start_config_fetch, elem, 0))
620 	rv = ENOMEM;
621 
622     if (rv)
623 	ipmi_mem_free(elem);
624     else
625 	solparm_get(solparm);
626 
627     return rv;
628 }
629 
630 typedef struct solparm_set_handler_s
631 {
632     ipmi_solparm_t 	 *solparm;
633     ipmi_solparm_done_cb handler;
634     void                 *cb_data;
635     unsigned char        data[MAX_IPMI_DATA_SIZE];
636     unsigned int         data_len;
637     int                  rv;
638 } solparm_set_handler_t;
639 
640 /* This should be called with the solparm locked.  It will unlock the solparm
641    before returning. */
642 static void
set_complete(ipmi_solparm_t * solparm,int err,solparm_set_handler_t * elem)643 set_complete(ipmi_solparm_t *solparm, int err, solparm_set_handler_t *elem)
644 {
645     if (solparm->in_destroy)
646 	goto out;
647 
648     solparm_unlock(solparm);
649 
650     if (elem->handler)
651 	elem->handler(solparm, err, elem->cb_data);
652 
653     ipmi_mem_free(elem);
654 
655     solparm_lock(solparm);
656     if (!solparm->destroyed) {
657 	solparm_unlock(solparm);
658 	opq_op_done(solparm->opq);
659     } else {
660 	solparm_unlock(solparm);
661     }
662 
663     solparm_put(solparm);
664     return;
665 
666  out:
667     solparm_unlock(solparm);
668     solparm_put(solparm);
669 }
670 
671 static void
solparm_config_set(ipmi_mc_t * mc,ipmi_msg_t * rsp,void * rsp_data)672 solparm_config_set(ipmi_mc_t  *mc,
673 		   ipmi_msg_t *rsp,
674 		   void       *rsp_data)
675 {
676     solparm_set_handler_t *elem = rsp_data;
677     ipmi_solparm_t        *solparm = elem->solparm;
678     int               rv;
679 
680     rv = check_solparm_response_param(solparm, mc, rsp, 1,
681 				      "solparm_config_set");
682 
683     solparm_lock(solparm);
684     set_complete(solparm, rv, elem);
685 }
686 
687 static void
start_config_set_cb(ipmi_mc_t * mc,void * cb_data)688 start_config_set_cb(ipmi_mc_t *mc, void *cb_data)
689 {
690     solparm_set_handler_t *elem = cb_data;
691     ipmi_solparm_t        *solparm = elem->solparm;
692     ipmi_msg_t        msg;
693     int               rv;
694 
695     solparm_lock(solparm);
696     if (solparm->destroyed) {
697 	ipmi_log(IPMI_LOG_ERR_INFO,
698 		 "%ssolparm.c(start_config_set_cb): "
699 		 "SOLPARM was destroyed while an operation was in progress",
700 		 MC_NAME(mc));
701 	set_complete(solparm, ECANCELED, elem);
702 	goto out;
703     }
704 
705     msg.netfn = IPMI_TRANSPORT_NETFN;
706     msg.cmd = IPMI_SET_SOL_CONFIGURATION_PARAMETERS;
707     msg.data = elem->data;
708     msg.data_len = elem->data_len;
709     rv = ipmi_mc_send_command(mc, 0, &msg, solparm_config_set, elem);
710 
711     if (rv) {
712 	ipmi_log(IPMI_LOG_ERR_INFO,
713 		 "%ssolparm.c(start_config_set_cb): "
714 		 "SOLPARM start_config_set: could not send cmd: %x",
715 		 MC_NAME(mc), rv);
716 	set_complete(solparm, ECANCELED, elem);
717 	goto out;
718     }
719 
720     solparm_unlock(solparm);
721  out:
722     return;
723 }
724 
725 static int
start_config_set(void * cb_data,int shutdown)726 start_config_set(void *cb_data, int shutdown)
727 {
728     solparm_set_handler_t *elem = cb_data;
729     int                   rv;
730 
731     if (shutdown) {
732 	ipmi_log(IPMI_LOG_ERR_INFO,
733 		 "solparm.c(start_config_set): "
734 		 "SOLPARM was destroyed while an operation was in progress");
735 	solparm_lock(elem->solparm);
736 	set_complete(elem->solparm, ECANCELED, elem);
737 	return OPQ_HANDLER_STARTED;
738     }
739 
740     /* The read lock must be claimed before the solparm lock to avoid
741        deadlock. */
742     rv = ipmi_mc_pointer_cb(elem->solparm->mc, start_config_set_cb, elem);
743     if (rv) {
744 	ipmi_log(IPMI_LOG_ERR_INFO,
745 		 "solparm.c(start_config_set): "
746 		 "SOLPARM's MC is not valid");
747 	solparm_lock(elem->solparm);
748 	set_complete(elem->solparm, rv, elem);
749     }
750     return OPQ_HANDLER_STARTED;
751 }
752 
753 int
ipmi_solparm_set_parm(ipmi_solparm_t * solparm,unsigned int parm,unsigned char * data,unsigned int data_len,ipmi_solparm_done_cb done,void * cb_data)754 ipmi_solparm_set_parm(ipmi_solparm_t       *solparm,
755 		      unsigned int         parm,
756 		      unsigned char        *data,
757 		      unsigned int         data_len,
758 		      ipmi_solparm_done_cb done,
759 		      void                 *cb_data)
760 {
761     solparm_set_handler_t *elem;
762     int               rv = 0;
763 
764     if (solparm->destroyed)
765 	return EINVAL;
766 
767     if (data_len > MAX_IPMI_DATA_SIZE-2)
768 	return EINVAL;
769 
770     elem = ipmi_mem_alloc(sizeof(*elem));
771     if (!elem) {
772 	ipmi_log(IPMI_LOG_ERR_INFO,
773 		 "solparm.c(ipmi_solparm_set_parm): "
774 		 "could not allocate the solparm element");
775 	return ENOMEM;
776     }
777 
778     elem->handler = done;
779     elem->cb_data = cb_data;
780     elem->solparm = solparm;
781     elem->data[0] = solparm->channel;
782     elem->data[1] = parm;
783     memcpy(elem->data+2, data, data_len);
784     elem->data_len = data_len + 2;
785     elem->rv = 0;
786 
787     if (!opq_new_op(solparm->opq, start_config_set, elem, 0))
788 	rv = ENOMEM;
789 
790     if (rv)
791 	ipmi_mem_free(elem);
792     else
793 	solparm_get(solparm);
794 
795     return rv;
796 }
797 
798 struct ipmi_sol_config_s
799 {
800     /* Stuff for getting/setting the values. */
801     int curr_parm;
802     int curr_sel;
803 
804     /* Not used for access, just for checking validity. */
805     ipmi_solparm_t *my_sol;
806 
807     /* Does this config hold the external SOL "set in progress" lock? */
808     int sol_locked;
809 
810     /* Does the SOL config support locking? */
811     int lock_supported;
812 
813     /* Used for deferred errors. */
814     int err;
815 
816     ipmi_solparm_done_cb   set_done;
817     ipmi_sol_get_config_cb done;
818     void                   *cb_data;
819 
820     unsigned int enable : 1;
821     unsigned int force_payload_encryption : 1;
822     unsigned int force_payload_authentication : 1;
823     unsigned int privilege_level : 4;
824     unsigned int retry_count : 3;
825     unsigned char payload_channel_supported;
826     unsigned char payload_channel;
827     unsigned char char_accumulation_interval;
828     unsigned char char_send_threshold;
829     unsigned char non_volatile_bitrate;
830     unsigned char volatile_bitrate;
831     unsigned char retry_interval;
832     unsigned char port_number_supported;
833     unsigned int port_number;
834 };
835 
836 typedef struct solparms_s solparms_t;
837 struct solparms_s
838 {
839     unsigned int valid : 1;
840     unsigned int optional_offset : 8;
841     unsigned int length : 8;
842     unsigned int offset : 8;
843     /* Returns err. */
844     int (*get_handler)(ipmi_sol_config_t *solc, solparms_t *lp, int err,
845 		       unsigned char *data);
846     /* NULL if parameter is read-only */
847     void (*set_handler)(ipmi_sol_config_t *solc, solparms_t *lp,
848 			unsigned char *data);
849 };
850 
851 /* SoL Enable */
gse(ipmi_sol_config_t * solc,solparms_t * lp,int err,unsigned char * data)852 static int gse(ipmi_sol_config_t *solc, solparms_t *lp, int err,
853 	       unsigned char *data)
854 {
855     if (err)
856 	return err;
857 
858     data++; /* Skip over the revision byte. */
859     solc->enable = data[0] & 0x1;
860     return 0;
861 }
862 
sse(ipmi_sol_config_t * solc,solparms_t * lp,unsigned char * data)863 static void sse(ipmi_sol_config_t *solc, solparms_t *lp, unsigned char *data)
864 {
865     data[0] = solc->enable;
866 }
867 
868 /* Authentication */
gsa(ipmi_sol_config_t * solc,solparms_t * lp,int err,unsigned char * data)869 static int gsa(ipmi_sol_config_t *solc, solparms_t *lp, int err,
870 	       unsigned char *data)
871 {
872     if (err)
873 	return err;
874 
875     data++; /* Skip over the revision byte. */
876     solc->force_payload_encryption = (data[0] >> 7) & 0x1;
877     solc->force_payload_authentication = (data[0] >> 6) & 0x1;
878     solc->privilege_level = data[0] & 0xf;
879     return 0;
880 }
881 
ssa(ipmi_sol_config_t * solc,solparms_t * lp,unsigned char * data)882 static void ssa(ipmi_sol_config_t *solc, solparms_t *lp, unsigned char *data)
883 {
884     data[0] = ((solc->force_payload_encryption << 7)
885 	       | (solc->force_payload_authentication << 6)
886 	       | solc->privilege_level);
887 }
888 
889 /* char settings */
gcs(ipmi_sol_config_t * solc,solparms_t * lp,int err,unsigned char * data)890 static int gcs(ipmi_sol_config_t *solc, solparms_t *lp, int err,
891 	       unsigned char *data)
892 {
893     if (err)
894 	return err;
895 
896     data++; /* Skip over the revision byte. */
897     solc->char_accumulation_interval = data[0];
898     solc->char_send_threshold = data[1];
899     return 0;
900 }
901 
scs(ipmi_sol_config_t * solc,solparms_t * lp,unsigned char * data)902 static void scs(ipmi_sol_config_t *solc, solparms_t *lp, unsigned char *data)
903 {
904     data[0] = solc->char_accumulation_interval;
905     data[1] = solc->char_send_threshold;
906 }
907 
908 /* retry */
gsr(ipmi_sol_config_t * solc,solparms_t * lp,int err,unsigned char * data)909 static int gsr(ipmi_sol_config_t *solc, solparms_t *lp, int err,
910 	       unsigned char *data)
911 {
912     if (err)
913 	return err;
914 
915     data++; /* Skip over the revision byte. */
916     solc->retry_count = data[0] & 0x7;
917     solc->retry_interval = data[1];
918     return 0;
919 }
920 
ssr(ipmi_sol_config_t * solc,solparms_t * lp,unsigned char * data)921 static void ssr(ipmi_sol_config_t *solc, solparms_t *lp, unsigned char *data)
922 {
923     data[0] = solc->retry_count;
924     data[1] = solc->retry_interval;
925 }
926 
927 /* bitrate */
gbr(ipmi_sol_config_t * solc,solparms_t * lp,int err,unsigned char * data)928 static int gbr(ipmi_sol_config_t *solc, solparms_t *lp, int err,
929 	       unsigned char *data)
930 {
931     unsigned char *ptr;
932 
933     if (err)
934 	return err;
935 
936     ptr = ((unsigned char *) solc) + lp->offset;
937 
938     data++; /* Skip over the revision byte. */
939     *ptr = data[0] & 0xf;
940     return 0;
941 }
942 
sbr(ipmi_sol_config_t * solc,solparms_t * lp,unsigned char * data)943 static void sbr(ipmi_sol_config_t *solc, solparms_t *lp, unsigned char *data)
944 {
945     unsigned char *ptr = ((unsigned char *) solc) + lp->offset;
946     data[0] = *ptr & 0xf;
947 }
948 
949 /* payload channel */
gpc(ipmi_sol_config_t * solc,solparms_t * lp,int err,unsigned char * data)950 static int gpc(ipmi_sol_config_t *solc, solparms_t *lp, int err,
951 	       unsigned char *data)
952 {
953     if (err) {
954 	if (err == IPMI_IPMI_ERR_VAL(0x80)) {
955 	    solc->payload_channel_supported = 0;
956 	    return 0;
957 	}
958 	return err;
959     }
960 
961     data++; /* Skip over the revision byte. */
962     solc->payload_channel_supported = 1;
963     solc->payload_channel = data[0] & 0xf;
964     return 0;
965 }
966 
967 /* port number */
gpn(ipmi_sol_config_t * solc,solparms_t * lp,int err,unsigned char * data)968 static int gpn(ipmi_sol_config_t *solc, solparms_t *lp, int err,
969 	       unsigned char *data)
970 {
971     if (err) {
972 	if (err == IPMI_IPMI_ERR_VAL(0x80)) {
973 	    solc->port_number_supported = 0;
974 	    return 0;
975 	}
976 	return err;
977     }
978 
979     data++; /* Skip over the revision byte. */
980     solc->port_number = ipmi_get_uint16(data);
981     return 0;
982 }
983 
spn(ipmi_sol_config_t * solc,solparms_t * lp,unsigned char * data)984 static void spn(ipmi_sol_config_t *solc, solparms_t *lp, unsigned char *data)
985 {
986     ipmi_set_uint16(data, solc->port_number);
987 }
988 
989 
990 #define OFFSET_OF(x) (((unsigned char *) &(((ipmi_sol_config_t *) NULL)->x)) \
991                       - ((unsigned char *) NULL))
992 
993 #define NUM_SOLPARMS 26
994 static solparms_t solparms[NUM_SOLPARMS] =
995 {
996     { 0, 0, 0, 0, NULL, NULL }, /* IPMI_SOLPARM_SET_IN_PROGRESS		     */
997     { 1, 0, 1, 0, gse,  sse  }, /* IPMI_SOLPARM_ENABLE			     */
998     { 1, 0, 1, 0, gsa,  ssa  }, /* IPMI_SOLPARM_AUTHENTICATION		     */
999     { 1, 0, 2, 0, gcs,  scs  }, /* IPMI_SOLPARM_CHAR_SETTINGS		     */
1000     { 1, 0, 2, 0, gsr,  ssr  }, /* IPMI_SOLPARM_RETRY			     */
1001 #define F OFFSET_OF(non_volatile_bitrate)
1002     { 1, 0, 1, F, gbr,  sbr  }, /* IPMI_SOLPARM_NONVOLATILE_BITRATE	     */
1003 #undef F
1004 #define F OFFSET_OF(volatile_bitrate)
1005     { 1, 0, 1, F, gbr,  sbr  }, /* IPMI_SOLPARM_VOLATILE_BITRATE	     */
1006 #undef F
1007 #define S OFFSET_OF(payload_channel_supported)
1008     { 1, S, 1, 0, gpc,  NULL }, /* IPMI_SOLPARM_PAYLOAD_CHANNEL		     */
1009 #undef S
1010 #define S OFFSET_OF(port_number_supported)
1011     { 1, S, 2, 0, gpn,  spn  }, /* IPMI_SOLPARM_PORT_NUMBER		     */
1012 #undef S
1013 };
1014 
1015 static void
err_lock_cleared(ipmi_solparm_t * solparm,int err,void * cb_data)1016 err_lock_cleared(ipmi_solparm_t *solparm,
1017 		 int            err,
1018 		 void           *cb_data)
1019 {
1020     ipmi_sol_config_t *solc = cb_data;
1021 
1022     if (solc->done)
1023 	solc->done(solparm, solc->err, NULL, solc->cb_data);
1024     ipmi_sol_free_config(solc);
1025     solparm->locked = 0;
1026     solparm_put(solparm);
1027 }
1028 
1029 static void
got_parm(ipmi_solparm_t * solparm,int err,unsigned char * data,unsigned int data_len,void * cb_data)1030 got_parm(ipmi_solparm_t    *solparm,
1031 	 int               err,
1032 	 unsigned char     *data,
1033 	 unsigned int      data_len,
1034 	 void              *cb_data)
1035 {
1036     ipmi_sol_config_t *solc = cb_data;
1037     solparms_t        *lp = &(solparms[solc->curr_parm]);
1038 
1039     /* Check the length, and don't forget the revision byte must be added. */
1040     if ((!err) && (data_len < (unsigned int) (lp->length+1))) {
1041 	if ((data_len == 1) && (lp->optional_offset)) {
1042 	    /* Some systems return zero-length data for optional parms. */
1043 	    unsigned char *opt = ((unsigned char *)solc) + lp->optional_offset;
1044 	    *opt = 0;
1045 	    goto next_parm;
1046 	}
1047 	ipmi_log(IPMI_LOG_ERR_INFO,
1048 		 "solparm.c(got_parm): "
1049 		 " Invalid data length on parm %d was %d, should have been %d",
1050 		 solc->curr_parm, data_len, lp->length+1);
1051 	err = EINVAL;
1052 	goto done;
1053     }
1054 
1055     err = lp->get_handler(solc, lp, err, data);
1056     if (err) {
1057 	ipmi_log(IPMI_LOG_ERR_INFO,
1058 		 "solparm.c(got_parm): "
1059 		 "Error fetching parm %d: %x",
1060 		 solc->curr_parm, err);
1061 	goto done;
1062     }
1063 
1064  next_parm:
1065     switch (solc->curr_parm) {
1066     case IPMI_SOLPARM_PAYLOAD_PORT_NUMBER:
1067 	goto done;
1068     default:
1069 	solc->curr_parm++;
1070     }
1071 
1072     lp = &(solparms[solc->curr_parm]);
1073     if (!lp->valid)
1074 	goto next_parm;
1075 
1076     err = ipmi_solparm_get_parm(solparm, solc->curr_parm, solc->curr_sel, 0,
1077 				got_parm, solc);
1078     if (err)
1079 	goto done;
1080 
1081     return;
1082 
1083  done:
1084     if (err) {
1085 	unsigned char data[1];
1086 
1087 	ipmi_log(IPMI_LOG_ERR_INFO,
1088 		 "solparm.c(got_parm): Error trying to get parm %d: %x",
1089 		 solc->curr_parm, err);
1090 	solc->err = err;
1091 	/* Clear the lock */
1092 	data[0] = 0;
1093 	err = ipmi_solparm_set_parm(solparm, 0, data, 1,
1094 				    err_lock_cleared, solc);
1095 	if (err) {
1096 	    ipmi_sol_free_config(solc);
1097 	    ipmi_log(IPMI_LOG_ERR_INFO,
1098 		     "solparm.c(got_parm): Error trying to clear lock: %x",
1099 		     err);
1100 	    solc->done(solparm, solc->err, NULL, solc->cb_data);
1101 	    ipmi_sol_free_config(solc);
1102 	    solparm->locked = 0;
1103 	    solparm_put(solparm);
1104 	}
1105     } else {
1106 	solc->done(solparm, 0, solc, solc->cb_data);
1107 	solparm_put(solparm);
1108     }
1109 }
1110 
1111 static void
lock_done(ipmi_solparm_t * solparm,int err,void * cb_data)1112 lock_done(ipmi_solparm_t *solparm,
1113 	  int            err,
1114 	  void           *cb_data)
1115 {
1116     ipmi_sol_config_t *solc = cb_data;
1117     int               rv;
1118 
1119     if (err == IPMI_IPMI_ERR_VAL(0x80)) {
1120 	/* Lock is not supported, just mark it and go on. */
1121 	solc->lock_supported = 0;
1122     } else if (err == IPMI_IPMI_ERR_VAL(0x81)) {
1123 	/* Someone else has the lock, return EAGAIN. */
1124 	solc->done(solparm, EAGAIN, NULL, solc->cb_data);
1125 	ipmi_sol_free_config(solc);
1126 	solparm_put(solparm);
1127 	return;
1128     } else if (err) {
1129 	ipmi_log(IPMI_LOG_ERR_INFO,
1130 		 "solparm.c(lock_done): Error trying to lock the SOL"
1131 		 " parms: %x",
1132 		 err);
1133 	solc->done(solparm, err, NULL, solc->cb_data);
1134 	ipmi_sol_free_config(solc);
1135 	solparm_put(solparm);
1136 	return;
1137     } else {
1138 	solc->sol_locked = 1;
1139 	solparm->locked = 1;
1140     }
1141 
1142     rv = ipmi_solparm_get_parm(solparm, solc->curr_parm, solc->curr_sel, 0,
1143 			       got_parm, solc);
1144     if (rv) {
1145 	unsigned char data[1];
1146 	ipmi_log(IPMI_LOG_ERR_INFO,
1147 		 "solparm.c(lock_done): Error trying to get parms: %x",
1148 		 err);
1149 
1150 	solc->err = rv;
1151 	/* Clear the lock */
1152 	data[0] = 0;
1153 	rv = ipmi_solparm_set_parm(solparm, 0, data, 1,
1154 				   err_lock_cleared, solc);
1155 	if (rv) {
1156 	    ipmi_log(IPMI_LOG_ERR_INFO,
1157 		     "solparm.c(lock_done): Error trying to clear lock: %x",
1158 		     err);
1159 	    solc->done(solparm, solc->err, NULL, solc->cb_data);
1160 	    ipmi_sol_free_config(solc);
1161 	    solparm->locked = 0;
1162 	    solparm_put(solparm);
1163 	}
1164     }
1165 }
1166 
ipmi_sol_get_config(ipmi_solparm_t * solparm,ipmi_sol_get_config_cb done,void * cb_data)1167 int ipmi_sol_get_config(ipmi_solparm_t         *solparm,
1168 			ipmi_sol_get_config_cb done,
1169 			void                   *cb_data)
1170 {
1171     ipmi_sol_config_t *solc;
1172     int               rv;
1173     unsigned char     data[1];
1174 
1175     solc = ipmi_mem_alloc(sizeof(*solc));
1176     if (!solc)
1177 	return ENOMEM;
1178     memset(solc, 0, sizeof(*solc));
1179 
1180     solc->curr_parm = 1;
1181     solc->curr_sel = 0;
1182     solc->done = done;
1183     solc->cb_data = cb_data;
1184     solc->my_sol = solparm;
1185     solc->lock_supported = 1; /* Assume it works */
1186 
1187     solparm_get(solparm);
1188 
1189     /* First grab the lock */
1190     data[0] = 1; /* Set in progress. */
1191     rv = ipmi_solparm_set_parm(solparm, 0, data, 1, lock_done, solc);
1192     if (rv) {
1193 	ipmi_sol_free_config(solc);
1194 	solparm_put(solparm);
1195     }
1196 
1197     return rv;
1198 }
1199 
1200 static void
set_clear(ipmi_solparm_t * solparm,int err,void * cb_data)1201 set_clear(ipmi_solparm_t *solparm,
1202 	 int            err,
1203 	 void           *cb_data)
1204 {
1205     ipmi_sol_config_t *solc = cb_data;
1206 
1207     if (solc->err)
1208 	err = solc->err;
1209     if (solc->set_done)
1210 	solc->set_done(solparm, err, solc->cb_data);
1211     ipmi_sol_free_config(solc);
1212     solparm->locked = 0;
1213     solparm_put(solparm);
1214 }
1215 
1216 static void
commit_done(ipmi_solparm_t * solparm,int err,void * cb_data)1217 commit_done(ipmi_solparm_t *solparm,
1218 	    int            err,
1219 	    void           *cb_data)
1220 {
1221     ipmi_sol_config_t *solc = cb_data;
1222     unsigned char     data[1];
1223     int               rv;
1224 
1225     /* Note that we ignore the error.  The commit done is optional,
1226        and must return an error if it is optional, so we just ignore
1227        the error and clear the field here. */
1228 
1229     /* Commit is done.  The IPMI spec says that it goes into the
1230        set-in-progress state after this, so we need to clear it. */
1231 
1232     data[0] = 0;
1233     rv = ipmi_solparm_set_parm(solparm, 0, data, 1, set_clear, solc);
1234     if (rv) {
1235 	ipmi_log(IPMI_LOG_WARNING,
1236 		 "solparm.c(commit_done): Error trying to clear the set in"
1237 		 " progress: %x",
1238 		 rv);
1239 	set_clear(solparm, err, solc);
1240     }
1241 }
1242 
1243 static void
set_done(ipmi_solparm_t * solparm,int err,void * cb_data)1244 set_done(ipmi_solparm_t *solparm,
1245 	 int            err,
1246 	 void           *cb_data)
1247 {
1248     ipmi_sol_config_t *solc = cb_data;
1249     unsigned char     data[MAX_IPMI_DATA_SIZE];
1250     solparms_t        *lp = &(solparms[solc->curr_parm]);
1251 
1252     if (err == IPMI_IPMI_ERR_VAL(0x82)) {
1253 	/* We attempted to write a read-only parameter that is not
1254 	   marked by the spec as read-only.  Just ignore it. */
1255 	err = 0;
1256     }
1257 
1258     if (err) {
1259 	ipmi_log(IPMI_LOG_ERR_INFO,
1260 		 "solparm.c(set_done): Error setting sol parm %d sel %d: %x",
1261 		 solc->curr_parm, solc->curr_sel, err);
1262 	goto done;
1263     }
1264 
1265  next_parm:
1266     switch (solc->curr_parm) {
1267     case IPMI_SOLPARM_PAYLOAD_PORT_NUMBER:
1268 	goto done;
1269     default:
1270 	solc->curr_parm++;
1271     }
1272 
1273     lp = &(solparms[solc->curr_parm]);
1274     if ((!lp->valid) || (!lp->set_handler)
1275 	|| (lp->optional_offset
1276 	    && !(((unsigned char *) solc)[lp->optional_offset])))
1277     {
1278 	/* The parameter is read-only or not supported, just go on. */
1279 	goto next_parm;
1280     }
1281 
1282     lp->set_handler(solc, lp, data);
1283     err = ipmi_solparm_set_parm(solparm, solc->curr_parm,
1284 				data, lp->length, set_done, solc);
1285     if (err)
1286 	goto done;
1287 
1288     return;
1289 
1290  done:
1291     if (!solc->lock_supported) {
1292 	/* No lock support, just finish the operation. */
1293 	set_clear(solparm, err, solc);
1294 	return;
1295     }
1296     else if (err) {
1297 	data[0] = 0; /* Don't commit the parameters. */
1298 	solc->err = err;
1299 	err = ipmi_solparm_set_parm(solparm, 0, data, 1, set_clear, solc);
1300     } else {
1301 	data[0] = 2; /* Commit the parameters. */
1302 	err = ipmi_solparm_set_parm(solparm, 0, data, 1, commit_done, solc);
1303     }
1304     if (err) {
1305 	ipmi_log(IPMI_LOG_WARNING,
1306 		 "solparm.c(set_done): Error trying to clear the set in"
1307 		 " progress: %x",
1308 		 err);
1309 	set_clear(solparm, err, solc);
1310     }
1311 }
1312 
1313 int
ipmi_sol_set_config(ipmi_solparm_t * solparm,ipmi_sol_config_t * osolc,ipmi_solparm_done_cb done,void * cb_data)1314 ipmi_sol_set_config(ipmi_solparm_t       *solparm,
1315 		    ipmi_sol_config_t    *osolc,
1316 		    ipmi_solparm_done_cb done,
1317 		    void                 *cb_data)
1318 {
1319     ipmi_sol_config_t *solc;
1320     unsigned char     data[MAX_IPMI_DATA_SIZE];
1321     solparms_t        *lp;
1322     int               rv;
1323 
1324     if (osolc->my_sol != solparm)
1325 	return EINVAL;
1326 
1327     if (!osolc->sol_locked)
1328 	return EINVAL;
1329 
1330     solc = ipmi_mem_alloc(sizeof(*solc));
1331     if (!solc)
1332 	return ENOMEM;
1333 
1334     *solc = *osolc;
1335     solc->err = 0;
1336     solc->sol_locked = 0; /* Set this here, since we will unlock it,
1337 			     but we don't want the free operation to
1338 			     attempt an unlock */
1339 
1340     solc->curr_parm = 1;
1341     solc->curr_sel = 0;
1342     solc->set_done = done;
1343     solc->cb_data = cb_data;
1344 
1345     /* Parm 1 is known good for writing. */
1346     lp = &(solparms[solc->curr_parm]);
1347     lp->set_handler(solc, lp, data);
1348     rv = ipmi_solparm_set_parm(solparm, solc->curr_parm,
1349 			       data, lp->length, set_done, solc);
1350     if (rv) {
1351 	ipmi_sol_free_config(solc);
1352     } else {
1353 	/* The old config no longer holds the lock. */
1354 	osolc->sol_locked = 0;
1355 	solparm_get(solparm);
1356     }
1357     return rv;
1358 }
1359 
1360 typedef struct clear_lock_s
1361 {
1362     ipmi_solparm_done_cb done;
1363     void                 *cb_data;
1364 
1365 } clear_lock_t;
1366 
1367 static void
lock_cleared(ipmi_solparm_t * solparm,int err,void * cb_data)1368 lock_cleared(ipmi_solparm_t *solparm,
1369 	     int            err,
1370 	     void           *cb_data)
1371 {
1372     clear_lock_t *cl = cb_data;
1373 
1374     cl->done(solparm, err, cl->cb_data);
1375 
1376     ipmi_mem_free(cl);
1377     solparm->locked = 0;
1378     solparm_put(solparm);
1379 }
1380 
1381 int
ipmi_sol_clear_lock(ipmi_solparm_t * solparm,ipmi_sol_config_t * solc,ipmi_solparm_done_cb done,void * cb_data)1382 ipmi_sol_clear_lock(ipmi_solparm_t       *solparm,
1383 		    ipmi_sol_config_t    *solc,
1384 		    ipmi_solparm_done_cb done,
1385 		    void                 *cb_data)
1386 {
1387     unsigned char data[1];
1388     int           rv;
1389     clear_lock_t  *cl;
1390 
1391     if (solc) {
1392 	if (solc->my_sol != solparm)
1393 	    return EINVAL;
1394 
1395 	if (!solc->sol_locked)
1396 	    return EINVAL;
1397     }
1398 
1399     cl = ipmi_mem_alloc(sizeof(*cl));
1400     if (!cl)
1401 	return ENOMEM;
1402     cl->done = done;
1403     cl->cb_data = cb_data;
1404 
1405     data[0] = 0; /* Clear the lock. */
1406     rv = ipmi_solparm_set_parm(solparm, 0, data, 1, lock_cleared, cl);
1407     if (rv) {
1408 	ipmi_mem_free(cl);
1409     } else {
1410 	if (solc)
1411 	    solc->sol_locked = 0;
1412 	solparm_get(solparm);
1413     }
1414 
1415     return rv;
1416 }
1417 
1418 void
ipmi_sol_free_config(ipmi_sol_config_t * solc)1419 ipmi_sol_free_config(ipmi_sol_config_t *solc)
1420 {
1421     ipmi_mem_free(solc);
1422 }
1423 
1424 
1425 #define LP_INT_PARM(n) \
1426 unsigned int \
1427 ipmi_solconfig_get_ ## n(ipmi_sol_config_t *solc) \
1428 { \
1429     return solc->n; \
1430 } \
1431 int \
1432 ipmi_solconfig_set_ ## n(ipmi_sol_config_t *solc, \
1433 			 unsigned int      val) \
1434 { \
1435     solc->n = val; \
1436     return 0; \
1437 }
1438 
1439 
1440 LP_INT_PARM(enable)
1441 LP_INT_PARM(force_payload_encryption)
1442 LP_INT_PARM(force_payload_authentication)
1443 LP_INT_PARM(privilege_level)
1444 LP_INT_PARM(char_accumulation_interval)
1445 LP_INT_PARM(char_send_threshold)
1446 LP_INT_PARM(retry_count)
1447 LP_INT_PARM(retry_interval)
1448 LP_INT_PARM(non_volatile_bitrate)
1449 LP_INT_PARM(volatile_bitrate)
1450 
1451 
1452 #define LP_INT_PARM_SUP(n, s) \
1453 int \
1454 ipmi_solconfig_get_ ## n(ipmi_sol_config_t *solc, \
1455 			 unsigned int      *data) \
1456 { \
1457     if (! solc->s) \
1458         return ENOSYS; \
1459     *data = solc->n; \
1460     return 0; \
1461 } \
1462 int \
1463 ipmi_solconfig_set_ ## n(ipmi_sol_config_t *solc, \
1464 			 unsigned int      data) \
1465 { \
1466     if (! solc->s) \
1467         return ENOSYS; \
1468     solc->n = data; \
1469     return 0; \
1470 }
1471 
1472 LP_INT_PARM_SUP(payload_channel, payload_channel_supported);
1473 LP_INT_PARM_SUP(port_number, port_number_supported)
1474 
1475 
1476 typedef struct solparm_gendata_s
1477 {
1478     enum ipmi_solconf_val_type_e datatype;
1479     char *fname;
1480 
1481     union {
1482 	struct {
1483 	    unsigned int (*gval)(ipmi_sol_config_t *solc);
1484 	    int (*gval_v)(ipmi_sol_config_t *solc, unsigned int *val);
1485 	    int (*gval_iv)(ipmi_sol_config_t *solc, unsigned int idx,
1486 			   unsigned int *val);
1487 	    int (*sval)(ipmi_sol_config_t *solc, unsigned int val);
1488 	    int (*sval_v)(ipmi_sol_config_t *solc, unsigned int val);
1489 	    int (*sval_iv)(ipmi_sol_config_t *solc, unsigned int idx,
1490 			   unsigned int val);
1491 	} ival;
1492 	struct {
1493 	    int (*gval_v)(ipmi_sol_config_t *solc, unsigned char *data,
1494 			  unsigned int *data_len);
1495 	    int (*gval_iv)(ipmi_sol_config_t *solc, unsigned int idx,
1496 			   unsigned char *data, unsigned int *data_len);
1497 	    int (*sval_v)(ipmi_sol_config_t *solc, unsigned char *data,
1498 			  unsigned int data_len);
1499 	    int (*sval_iv)(ipmi_sol_config_t *solc, unsigned int idx,
1500 			   unsigned char *data, unsigned int data_len);
1501 	} dval;
1502     } u;
1503     unsigned int (*iv_cnt)(ipmi_sol_config_t *solc);
1504 } solparm_gendata_t;
1505 
1506 #define F_BOOL(name) \
1507 	{ .datatype = IPMI_SOLCONFIG_BOOL, .fname = #name, \
1508 	  .u = { .ival = { .gval = ipmi_solconfig_get_ ## name, \
1509 			   .sval = ipmi_solconfig_set_ ## name }}}
1510 #define F_INT(name) \
1511 	{ .datatype = IPMI_SOLCONFIG_INT, .fname = #name, \
1512 	  .u = { .ival = { .gval = ipmi_solconfig_get_ ## name, \
1513 			   .sval = ipmi_solconfig_set_ ## name }}}
1514 #define F_INTV(name) \
1515 	{ .datatype = IPMI_SOLCONFIG_INT, .fname = #name, \
1516 	  .u = { .ival = { .gval_v = ipmi_solconfig_get_ ## name, \
1517 			   .sval_v = ipmi_solconfig_set_ ## name }}}
1518 
1519 static solparm_gendata_t gdata[] =
1520 {
1521     F_BOOL(enable),				/* 0 */
1522     F_BOOL(force_payload_encryption),
1523     F_BOOL(force_payload_authentication),
1524     F_INT(privilege_level),
1525     F_INT(retry_count),
1526     F_INT(retry_interval),			/* 5 */
1527     F_INT(char_accumulation_interval),
1528     F_INT(char_send_threshold),
1529     F_INT(non_volatile_bitrate),
1530     F_INT(volatile_bitrate),
1531     F_INTV(payload_channel),			/* 10 */
1532     F_INTV(port_number),
1533 };
1534 #define NUM_GDATA_ENTRIES (sizeof(gdata) / sizeof(solparm_gendata_t))
1535 
1536 int
ipmi_solconfig_enum_val(unsigned int parm,int val,int * nval,const char ** sval)1537 ipmi_solconfig_enum_val(unsigned int parm, int val, int *nval,
1538 			const char **sval)
1539 {
1540     char *rval;
1541     int  rnval;
1542 
1543     switch (parm) {
1544     case 3: /* privilege level */
1545 	if (val < 2) {
1546 	    if (nval)
1547 		*nval = 2;
1548 	    return EINVAL;
1549 	}
1550 
1551 	switch (val) {
1552 	case 2:
1553 	    rval = "user";
1554 	    rnval = 3;
1555 	    break;
1556 	case 3:
1557 	    rval = "operator";
1558 	    rnval = 4;
1559 	    break;
1560 	case 4:
1561 	    rval = "admin";
1562 	    rnval = 5;
1563 	    break;
1564 	case 5:
1565 	    rval = "oem";
1566 	    rnval = -1;
1567 	    break;
1568 	default:
1569 	    if (*nval)
1570 		*nval = -1;
1571 	    return EINVAL;
1572 	}
1573 	break;
1574 
1575     case 8: case 9:
1576 	if (val < 6) {
1577 	    if (nval)
1578 		*nval = 6;
1579 	    return EINVAL;
1580 	}
1581 
1582 	switch (val) {
1583 	case 6:
1584 	    rval = "9600";
1585 	    rnval = 7;
1586 	    break;
1587 	case 7:
1588 	    rval = "19.2K";
1589 	    rnval = 8;
1590 	    break;
1591 	case 8:
1592 	    rval = "38.4K";
1593 	    rnval = 9;
1594 	    break;
1595 	case 9:
1596 	    rval = "57.6K";
1597 	    rnval = 10;
1598 	    break;
1599 	case 10:
1600 	    rval = "115.2K";
1601 	    rnval = -1;
1602 	    break;
1603 	default:
1604 	    if (*nval)
1605 		*nval = -1;
1606 	    return EINVAL;
1607 	}
1608 	break;
1609 
1610     default:
1611 	return ENOSYS;
1612     }
1613 
1614 
1615     if (sval)
1616 	*sval = rval;
1617     if (nval)
1618 	*nval = rnval;
1619     return 0;
1620 }
1621 
1622 int
ipmi_solconfig_enum_idx(unsigned int parm,int idx,const char ** sval)1623 ipmi_solconfig_enum_idx(unsigned int parm, int idx, const char **sval)
1624 {
1625     return ENOSYS;
1626 }
1627 
1628 int
ipmi_solconfig_get_val(ipmi_sol_config_t * solc,unsigned int parm,const char ** name,int * index,enum ipmi_solconf_val_type_e * valtype,unsigned int * ival,unsigned char ** dval,unsigned int * dval_len)1629 ipmi_solconfig_get_val(ipmi_sol_config_t *solc,
1630 		       unsigned int      parm,
1631 		       const char        **name,
1632 		       int               *index,
1633 		       enum ipmi_solconf_val_type_e *valtype,
1634 		       unsigned int      *ival,
1635 		       unsigned char     **dval,
1636 		       unsigned int      *dval_len)
1637 {
1638     unsigned int  curr = *index;
1639     unsigned int  count;
1640     int           rv = 0;
1641     unsigned char *data;
1642     unsigned int  data_len;
1643 
1644     if (parm >= NUM_GDATA_ENTRIES)
1645 	return EINVAL;
1646     if (valtype)
1647 	*valtype = gdata[parm].datatype;
1648     if (name)
1649 	*name = gdata[parm].fname;
1650 
1651     if (gdata[parm].iv_cnt) {
1652 	count = gdata[parm].iv_cnt(solc);
1653 	if (curr >= count) {
1654 	    *index = -1;
1655 	    return E2BIG;
1656 	}
1657 
1658 	if (curr+1 == count)
1659 	    *index = -1;
1660 	else
1661 	    *index = curr+1;
1662     }
1663 
1664     switch (gdata[parm].datatype) {
1665     case IPMI_SOLCONFIG_INT:
1666     case IPMI_SOLCONFIG_BOOL:
1667 	if (!ival)
1668 	    break;
1669 	if (gdata[parm].u.ival.gval)
1670 	    *ival = gdata[parm].u.ival.gval(solc);
1671 	else if (gdata[parm].u.ival.gval_v)
1672 	    rv = gdata[parm].u.ival.gval_v(solc, ival);
1673 	else if (gdata[parm].u.ival.gval_iv)
1674 	    rv = gdata[parm].u.ival.gval_iv(solc, curr, ival);
1675 	else
1676 	    rv = ENOSYS;
1677 	break;
1678 
1679     case IPMI_SOLCONFIG_DATA:
1680     case IPMI_SOLCONFIG_IP:
1681     case IPMI_SOLCONFIG_MAC:
1682 	data_len = 0;
1683 	if (gdata[parm].u.dval.gval_v)
1684 	    rv = gdata[parm].u.dval.gval_v(solc, NULL, &data_len);
1685 	else if (gdata[parm].u.dval.gval_iv)
1686 	    rv = gdata[parm].u.dval.gval_iv(solc, curr, NULL, &data_len);
1687 	else
1688 	    rv = ENOSYS;
1689 	if (rv && (rv != EBADF))
1690 	    break;
1691 	if (data_len == 0)
1692 	    data = ipmi_mem_alloc(1);
1693 	else
1694 	    data = ipmi_mem_alloc(data_len);
1695 	if (gdata[parm].u.dval.gval_v)
1696 	    rv = gdata[parm].u.dval.gval_v(solc, data, &data_len);
1697 	else if (gdata[parm].u.dval.gval_iv)
1698 	    rv = gdata[parm].u.dval.gval_iv(solc, curr, data, &data_len);
1699 	if (rv) {
1700 	    ipmi_mem_free(data);
1701 	    break;
1702 	}
1703 	if (dval)
1704 	    *dval = data;
1705 	if (dval_len)
1706 	    *dval_len = data_len;
1707 	break;
1708     }
1709 
1710     return rv;
1711 }
1712 
1713 int
ipmi_solconfig_set_val(ipmi_sol_config_t * solc,unsigned int parm,int index,unsigned int ival,unsigned char * dval,unsigned int dval_len)1714 ipmi_solconfig_set_val(ipmi_sol_config_t *solc,
1715 		       unsigned int      parm,
1716 		       int               index,
1717 		       unsigned int      ival,
1718 		       unsigned char     *dval,
1719 		       unsigned int      dval_len)
1720 {
1721     unsigned int  count;
1722     int           rv = 0;
1723 
1724     if (parm >= NUM_GDATA_ENTRIES)
1725 	return EINVAL;
1726 
1727     if (gdata[parm].iv_cnt) {
1728 	count = gdata[parm].iv_cnt(solc);
1729 	if (index >= (int) count)
1730 	    return E2BIG;
1731     }
1732 
1733     switch (gdata[parm].datatype) {
1734     case IPMI_SOLCONFIG_INT:
1735     case IPMI_SOLCONFIG_BOOL:
1736 	if (gdata[parm].u.ival.sval)
1737 
1738 	    rv = gdata[parm].u.ival.sval(solc, ival);
1739 	else if (gdata[parm].u.ival.sval_v)
1740 	    rv = gdata[parm].u.ival.sval_v(solc, ival);
1741 	else if (gdata[parm].u.ival.sval_iv)
1742 	    rv = gdata[parm].u.ival.sval_iv(solc, index, ival);
1743 	else
1744 	    rv = ENOSYS;
1745 	break;
1746 
1747     case IPMI_SOLCONFIG_DATA:
1748     case IPMI_SOLCONFIG_IP:
1749     case IPMI_SOLCONFIG_MAC:
1750 	if (gdata[parm].u.dval.sval_v)
1751 	    rv = gdata[parm].u.dval.sval_v(solc, dval, dval_len);
1752 	else if (gdata[parm].u.dval.sval_iv)
1753 	    rv = gdata[parm].u.dval.sval_iv(solc, index, dval, dval_len);
1754 	else
1755 	    rv = ENOSYS;
1756 	break;
1757     }
1758 
1759     return rv;
1760 }
1761 
1762 
1763 void
ipmi_solconfig_data_free(void * data)1764 ipmi_solconfig_data_free(void *data)
1765 {
1766     ipmi_mem_free(data);
1767 }
1768 
1769 unsigned int
ipmi_solconfig_str_to_parm(char * name)1770 ipmi_solconfig_str_to_parm(char *name)
1771 {
1772     unsigned int i;
1773     for (i=0; i<NUM_GDATA_ENTRIES; i++) {
1774 	if (strcmp(name, gdata[i].fname) == 0)
1775 	    return i;
1776     }
1777     return -1;
1778 }
1779 
1780 const char *
ipmi_solconfig_parm_to_str(unsigned int parm)1781 ipmi_solconfig_parm_to_str(unsigned int parm)
1782 {
1783     if (parm >= NUM_GDATA_ENTRIES)
1784 	return NULL;
1785     return gdata[parm].fname;
1786 }
1787 
1788 int
ipmi_solconfig_parm_to_type(unsigned int parm,enum ipmi_solconf_val_type_e * valtype)1789 ipmi_solconfig_parm_to_type(unsigned int                 parm,
1790 			    enum ipmi_solconf_val_type_e *valtype)
1791 {
1792     if (parm >= NUM_GDATA_ENTRIES)
1793 	return EINVAL;
1794     *valtype = gdata[parm].datatype;
1795     return 0;
1796 }
1797