summaryrefslogtreecommitdiff
path: root/usr/src/lib/libipmi/common/ipmi_lancfg.c
blob: 3e3ebc6e8163cef869832fcb3e1a2bb513677a7d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Query and configure LAN interfaces over IPMI.  This is done through the
 * complicated get/set LAN Configuration Parameters command.  This queries or
 * sets the parameters one per command in series.  We hide this implementation
 * detail and instead export a single structure to consumers.
 */

#include <stddef.h>
#include <strings.h>

#include <libipmi.h>

#include "ipmi_impl.h"

typedef struct ipmi_cmd_lan_get_config {
	DECL_BITFIELD3(
	    ilgc_number		:4,
	    __reserved		:3,
	    ilgc_revonly	:1);
	uint8_t		ilgc_param;
	uint8_t		ilgc_set;
	uint8_t		ilgc_block;
} ipmi_cmd_lan_get_config_t;

typedef struct ipmi_cmd_lan_set_config {
	DECL_BITFIELD2(
	    ilsc_number		:4,
	    __reserved		:4);
	uint8_t		ilsc_param;
	uint8_t		ilsc_data[18];
} ipmi_cmd_lan_set_config_t;

#define	IPMI_LAN_SET_LEN(dlen)	\
	(offsetof(ipmi_cmd_lan_set_config_t, ilsc_data) + (dlen))

#define	IPMI_LAN_PARAM_SET_IN_PROGRESS		0
#define	IPMI_LAN_PARAM_IP_ADDR			3
#define	IPMI_LAN_PARAM_IP_SOURCE		4
#define	IPMI_LAN_PARAM_MAC_ADDR			5
#define	IPMI_LAN_PARAM_SUBNET_MASK		6
#define	IPMI_LAN_PARAM_GATEWAY_ADDR		12

#define	IPMI_LAN_SET_COMPLETE			0x0
#define	IPMI_LAN_SET_INPROGRESS			0x1
#define	IPMI_LAN_SET_COMMIT			0x2

typedef struct ipmi_lan_entry {
	int	ile_param;
	int	ile_mask;
	int	ile_set;
	int	ile_block;
	size_t	ile_offset;
	size_t	ile_len;
} ipmi_lan_entry_t;

static ipmi_lan_entry_t ipmi_lan_table[] = {
	{ IPMI_LAN_PARAM_IP_ADDR, IPMI_LAN_SET_IPADDR, 0, 0,
	    offsetof(ipmi_lan_config_t, ilc_ipaddr), sizeof (uint32_t) },
	{ IPMI_LAN_PARAM_IP_SOURCE, IPMI_LAN_SET_IPADDR_SOURCE, 0, 0,
	    offsetof(ipmi_lan_config_t, ilc_ipaddr_source), sizeof (uint8_t) },
	{ IPMI_LAN_PARAM_MAC_ADDR, IPMI_LAN_SET_MACADDR, 0, 0,
	    offsetof(ipmi_lan_config_t, ilc_macaddr), 6 * sizeof (uint8_t) },
	{ IPMI_LAN_PARAM_SUBNET_MASK, IPMI_LAN_SET_SUBNET, 0, 0,
	    offsetof(ipmi_lan_config_t, ilc_subnet), sizeof (uint32_t) },
	{ IPMI_LAN_PARAM_GATEWAY_ADDR, IPMI_LAN_SET_GATEWAY_ADDR, 0, 0,
	    offsetof(ipmi_lan_config_t, ilc_gateway_addr), sizeof (uint32_t) }
};

#define	IPMI_LAN_NENTRIES	\
	(sizeof (ipmi_lan_table) / sizeof (ipmi_lan_table[0]))

static int
ipmi_lan_get_param(ipmi_handle_t *ihp, int channel, int param, int set,
    int block, void *data, size_t len)
{
	ipmi_cmd_t cmd, *rsp;
	ipmi_cmd_lan_get_config_t lcmd = { 0 };

	lcmd.ilgc_number = channel;
	lcmd.ilgc_param = param;
	lcmd.ilgc_set = set;
	lcmd.ilgc_block = block;

	cmd.ic_netfn = IPMI_NETFN_TRANSPORT;
	cmd.ic_lun = 0;
	cmd.ic_cmd = IPMI_CMD_GET_LAN_CONFIG;
	cmd.ic_data = &lcmd;
	cmd.ic_dlen = sizeof (lcmd);

	if ((rsp = ipmi_send(ihp, &cmd)) == NULL) {
		switch (ihp->ih_completion) {
		case 0x80:
			(void) ipmi_set_error(ihp, EIPMI_BADPARAM, NULL);
			break;
		}
		return (-1);
	}

	if (rsp->ic_dlen < len + 1)
		return (ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL));

	bcopy((uint8_t *)rsp->ic_data + 1, data, len);

	return (0);
}

int
ipmi_lan_get_config(ipmi_handle_t *ihp, int channel, ipmi_lan_config_t *cfgp)
{
	uint8_t set;
	int i;
	ipmi_lan_entry_t *lep;

	if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS, 0,
	    0, &set, sizeof (set)) != 0)
		return (-1);

	if (set & IPMI_LAN_SET_INPROGRESS)
		cfgp->ilc_set_in_progress = B_TRUE;
	else
		cfgp->ilc_set_in_progress = B_FALSE;

	for (i = 0; i < IPMI_LAN_NENTRIES; i++) {
		lep = &ipmi_lan_table[i];
		if (ipmi_lan_get_param(ihp, channel, lep->ile_param,
		    lep->ile_set, lep->ile_block,
		    (char *)cfgp + lep->ile_offset, lep->ile_len) != 0)
			return (-1);
	}

	return (0);
}

static int
ipmi_lan_set_param(ipmi_handle_t *ihp, int channel, int param, void *data,
    size_t len)
{
	ipmi_cmd_t cmd;
	ipmi_cmd_lan_set_config_t lcmd = { 0 };

	lcmd.ilsc_number = channel;
	lcmd.ilsc_param = param;
	bcopy(data, lcmd.ilsc_data, len);

	cmd.ic_netfn = IPMI_NETFN_TRANSPORT;
	cmd.ic_lun = 0;
	cmd.ic_cmd = IPMI_CMD_SET_LAN_CONFIG;
	cmd.ic_data = &lcmd;
	cmd.ic_dlen = IPMI_LAN_SET_LEN(len);

	if (ipmi_send(ihp, &cmd) == NULL) {
		switch (ihp->ih_completion) {
		case 0x80:
			(void) ipmi_set_error(ihp, EIPMI_BADPARAM, NULL);
			break;

		case 0x81:
			(void) ipmi_set_error(ihp, EIPMI_BUSY, NULL);
			break;

		case 0x82:
			(void) ipmi_set_error(ihp, EIPMI_READONLY, NULL);
			break;

		case 0x83:
			(void) ipmi_set_error(ihp, EIPMI_WRITEONLY, NULL);
			break;
		}
		return (-1);
	}

	return (0);
}

int
ipmi_lan_set_config(ipmi_handle_t *ihp, int channel, ipmi_lan_config_t *cfgp,
    int mask)
{
	uint8_t set;
	int i;
	ipmi_lan_entry_t *lep;

	/*
	 * Cancel any pending transaction, then open a new transaction.
	 */
	set = IPMI_LAN_SET_COMPLETE;
	if (ipmi_lan_set_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS,
	    &set, sizeof (set)) != 0)
		return (-1);
	set = IPMI_LAN_SET_INPROGRESS;
	if (ipmi_lan_set_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS,
	    &set, sizeof (set)) != 0)
		return (-1);

	/*
	 * Iterate over all parameters and set them.
	 */
	for (i = 0; i < IPMI_LAN_NENTRIES; i++) {
		lep = &ipmi_lan_table[i];
		if (!(lep->ile_mask & mask))
			continue;

		if (ipmi_lan_set_param(ihp, channel, lep->ile_param,
		    (char *)cfgp + lep->ile_offset, lep->ile_len) != 0) {
			/*
			 * On some systems, setting the mode to DHCP may cause
			 * the command to timeout, presumably because it is
			 * waiting for the setting to take effect.  If we see
			 * completion code 0xc3 (command timeout) while setting
			 * the DHCP value, just ignore it.
			 */
			if (mask != IPMI_LAN_SET_IPADDR_SOURCE ||
			    cfgp->ilc_ipaddr_source != IPMI_LAN_SRC_DHCP ||
			    ihp->ih_completion != 0xC3)
				return (-1);
		}
	}

	/*
	 * Commit the transaction.
	 */
	set = IPMI_LAN_SET_COMPLETE;
	if (ipmi_lan_set_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS,
	    &set, sizeof (set)) != 0)
		return (-1);

	return (0);
}