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 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * Query and configure LAN interfaces over IPMI.  This is done through the
28 * complicated get/set LAN Configuration Parameters command.  This queries or
29 * sets the parameters one per command in series.  We hide this implementation
30 * detail and instead export a single structure to consumers.
31 */
32
33#include <stddef.h>
34#include <strings.h>
35
36#include <libipmi.h>
37
38#include "ipmi_impl.h"
39
40typedef struct ipmi_cmd_lan_get_config {
41	DECL_BITFIELD3(
42	    ilgc_number		:4,
43	    __reserved		:3,
44	    ilgc_revonly	:1);
45	uint8_t		ilgc_param;
46	uint8_t		ilgc_set;
47	uint8_t		ilgc_block;
48} ipmi_cmd_lan_get_config_t;
49
50typedef struct ipmi_cmd_lan_set_config {
51	DECL_BITFIELD2(
52	    ilsc_number		:4,
53	    __reserved		:4);
54	uint8_t		ilsc_param;
55	uint8_t		ilsc_data[18];
56} ipmi_cmd_lan_set_config_t;
57
58#define	IPMI_LAN_SET_LEN(dlen)	\
59	(offsetof(ipmi_cmd_lan_set_config_t, ilsc_data) + (dlen))
60
61#define	IPMI_LAN_PARAM_SET_IN_PROGRESS		0
62#define	IPMI_LAN_PARAM_IP_ADDR			3
63#define	IPMI_LAN_PARAM_IP_SOURCE		4
64#define	IPMI_LAN_PARAM_MAC_ADDR			5
65#define	IPMI_LAN_PARAM_SUBNET_MASK		6
66#define	IPMI_LAN_PARAM_GATEWAY_ADDR		12
67
68#define	IPMI_LAN_SET_COMPLETE			0x0
69#define	IPMI_LAN_SET_INPROGRESS			0x1
70#define	IPMI_LAN_SET_COMMIT			0x2
71
72typedef struct ipmi_lan_entry {
73	int	ile_param;
74	int	ile_mask;
75	int	ile_set;
76	int	ile_block;
77	size_t	ile_offset;
78	size_t	ile_len;
79} ipmi_lan_entry_t;
80
81static ipmi_lan_entry_t ipmi_lan_table[] = {
82	{ IPMI_LAN_PARAM_IP_ADDR, IPMI_LAN_SET_IPADDR, 0, 0,
83	    offsetof(ipmi_lan_config_t, ilc_ipaddr), sizeof (uint32_t) },
84	{ IPMI_LAN_PARAM_IP_SOURCE, IPMI_LAN_SET_IPADDR_SOURCE, 0, 0,
85	    offsetof(ipmi_lan_config_t, ilc_ipaddr_source), sizeof (uint8_t) },
86	{ IPMI_LAN_PARAM_MAC_ADDR, IPMI_LAN_SET_MACADDR, 0, 0,
87	    offsetof(ipmi_lan_config_t, ilc_macaddr), 6 * sizeof (uint8_t) },
88	{ IPMI_LAN_PARAM_SUBNET_MASK, IPMI_LAN_SET_SUBNET, 0, 0,
89	    offsetof(ipmi_lan_config_t, ilc_subnet), sizeof (uint32_t) },
90	{ IPMI_LAN_PARAM_GATEWAY_ADDR, IPMI_LAN_SET_GATEWAY_ADDR, 0, 0,
91	    offsetof(ipmi_lan_config_t, ilc_gateway_addr), sizeof (uint32_t) }
92};
93
94#define	IPMI_LAN_NENTRIES	\
95	(sizeof (ipmi_lan_table) / sizeof (ipmi_lan_table[0]))
96
97static int
98ipmi_lan_get_param(ipmi_handle_t *ihp, int channel, int param, int set,
99    int block, void *data, size_t len)
100{
101	ipmi_cmd_t cmd, *rsp;
102	ipmi_cmd_lan_get_config_t lcmd = { 0 };
103
104	lcmd.ilgc_number = channel;
105	lcmd.ilgc_param = param;
106	lcmd.ilgc_set = set;
107	lcmd.ilgc_block = block;
108
109	cmd.ic_netfn = IPMI_NETFN_TRANSPORT;
110	cmd.ic_lun = 0;
111	cmd.ic_cmd = IPMI_CMD_GET_LAN_CONFIG;
112	cmd.ic_data = &lcmd;
113	cmd.ic_dlen = sizeof (lcmd);
114
115	if ((rsp = ipmi_send(ihp, &cmd)) == NULL) {
116		switch (ihp->ih_completion) {
117		case 0x80:
118			(void) ipmi_set_error(ihp, EIPMI_BADPARAM, NULL);
119			break;
120		}
121		return (-1);
122	}
123
124	if (rsp->ic_dlen < len + 1)
125		return (ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL));
126
127	bcopy((uint8_t *)rsp->ic_data + 1, data, len);
128
129	return (0);
130}
131
132int
133ipmi_lan_get_config(ipmi_handle_t *ihp, int channel, ipmi_lan_config_t *cfgp)
134{
135	uint8_t set;
136	int i;
137	ipmi_lan_entry_t *lep;
138
139	if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS, 0,
140	    0, &set, sizeof (set)) != 0)
141		return (-1);
142
143	if (set & IPMI_LAN_SET_INPROGRESS)
144		cfgp->ilc_set_in_progress = B_TRUE;
145	else
146		cfgp->ilc_set_in_progress = B_FALSE;
147
148	for (i = 0; i < IPMI_LAN_NENTRIES; i++) {
149		lep = &ipmi_lan_table[i];
150		if (ipmi_lan_get_param(ihp, channel, lep->ile_param,
151		    lep->ile_set, lep->ile_block,
152		    (char *)cfgp + lep->ile_offset, lep->ile_len) != 0)
153			return (-1);
154	}
155
156	return (0);
157}
158
159static int
160ipmi_lan_set_param(ipmi_handle_t *ihp, int channel, int param, void *data,
161    size_t len)
162{
163	ipmi_cmd_t cmd;
164	ipmi_cmd_lan_set_config_t lcmd = { 0 };
165
166	lcmd.ilsc_number = channel;
167	lcmd.ilsc_param = param;
168	bcopy(data, lcmd.ilsc_data, len);
169
170	cmd.ic_netfn = IPMI_NETFN_TRANSPORT;
171	cmd.ic_lun = 0;
172	cmd.ic_cmd = IPMI_CMD_SET_LAN_CONFIG;
173	cmd.ic_data = &lcmd;
174	cmd.ic_dlen = IPMI_LAN_SET_LEN(len);
175
176	if (ipmi_send(ihp, &cmd) == NULL) {
177		switch (ihp->ih_completion) {
178		case 0x80:
179			(void) ipmi_set_error(ihp, EIPMI_BADPARAM, NULL);
180			break;
181
182		case 0x81:
183			(void) ipmi_set_error(ihp, EIPMI_BUSY, NULL);
184			break;
185
186		case 0x82:
187			(void) ipmi_set_error(ihp, EIPMI_READONLY, NULL);
188			break;
189
190		case 0x83:
191			(void) ipmi_set_error(ihp, EIPMI_WRITEONLY, NULL);
192			break;
193		}
194		return (-1);
195	}
196
197	return (0);
198}
199
200int
201ipmi_lan_set_config(ipmi_handle_t *ihp, int channel, ipmi_lan_config_t *cfgp,
202    int mask)
203{
204	uint8_t set;
205	int i;
206	ipmi_lan_entry_t *lep;
207
208	/*
209	 * Cancel any pending transaction, then open a new transaction.
210	 */
211	set = IPMI_LAN_SET_COMPLETE;
212	if (ipmi_lan_set_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS,
213	    &set, sizeof (set)) != 0)
214		return (-1);
215	set = IPMI_LAN_SET_INPROGRESS;
216	if (ipmi_lan_set_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS,
217	    &set, sizeof (set)) != 0)
218		return (-1);
219
220	/*
221	 * Iterate over all parameters and set them.
222	 */
223	for (i = 0; i < IPMI_LAN_NENTRIES; i++) {
224		lep = &ipmi_lan_table[i];
225		if (!(lep->ile_mask & mask))
226			continue;
227
228		if (ipmi_lan_set_param(ihp, channel, lep->ile_param,
229		    (char *)cfgp + lep->ile_offset, lep->ile_len) != 0) {
230			/*
231			 * On some systems, setting the mode to DHCP may cause
232			 * the command to timeout, presumably because it is
233			 * waiting for the setting to take effect.  If we see
234			 * completion code 0xc3 (command timeout) while setting
235			 * the DHCP value, just ignore it.
236			 */
237			if (mask != IPMI_LAN_SET_IPADDR_SOURCE ||
238			    cfgp->ilc_ipaddr_source != IPMI_LAN_SRC_DHCP ||
239			    ihp->ih_completion != 0xC3)
240				return (-1);
241		}
242	}
243
244	/*
245	 * Commit the transaction.
246	 */
247	set = IPMI_LAN_SET_COMPLETE;
248	if (ipmi_lan_set_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS,
249	    &set, sizeof (set)) != 0)
250		return (-1);
251
252	return (0);
253}
254