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 2006 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 <sys/types.h>
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/promif_impl.h>
33 #include <sys/ds.h>
34 #include <sys/modctl.h>
35 #include <sys/ksynch.h>
36 #include <sys/varconfig.h>
37 
38 #ifndef _KMDB
39 
40 #define	PROMIF_DS_TIMEOUT_SEC 15
41 
42 static kmutex_t promif_prop_lock;
43 static kcondvar_t promif_prop_cv;
44 static var_config_msg_t promif_ds_resp;
45 static var_config_resp_t *cfg_rsp = &promif_ds_resp.var_config_resp;
46 static int (*ds_send)();
47 static int (*ds_init)();
48 
49 /*
50  * Domains Services interaction
51  */
52 static ds_svc_hdl_t	ds_primary_handle;
53 static ds_svc_hdl_t	ds_backup_handle;
54 
55 static ds_ver_t		vc_version[] = { { 1, 0 } };
56 
57 #define	VC_NVERS	(sizeof (vc_version) / sizeof (vc_version[0]))
58 
59 static ds_capability_t vc_primary_cap = {
60 	"var-config",		/* svc_id */
61 	vc_version,		/* vers */
62 	VC_NVERS		/* nvers */
63 };
64 
65 static ds_capability_t vc_backup_cap = {
66 	"var-config-backup",	/* svc_id */
67 	vc_version,		/* vers */
68 	VC_NVERS		/* nvers */
69 };
70 
71 static void vc_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
72 static void vc_unreg_handler(ds_cb_arg_t);
73 static void vc_data_handler(ds_cb_arg_t, void *, size_t);
74 
75 static ds_clnt_ops_t vc_primary_ops = {
76 	vc_reg_handler,		/* ds_primary_reg_cb */
77 	vc_unreg_handler,	/* ds_primary_unreg_cb */
78 	vc_data_handler,	/* ds_data_cb */
79 	&ds_primary_handle	/* cb_arg */
80 };
81 
82 static ds_clnt_ops_t vc_backup_ops = {
83 	vc_reg_handler,		/* ds_backup_reg_cb */
84 	vc_unreg_handler,	/* ds_backup_unreg_cb */
85 	vc_data_handler,	/* ds_data_cb */
86 	&ds_backup_handle	/* cb_arg */
87 };
88 
89 static void
90 vc_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
91 {
92 	_NOTE(ARGUNUSED(ver))
93 
94 	if ((ds_svc_hdl_t *)arg == &ds_primary_handle)
95 		ds_primary_handle = hdl;
96 	else if ((ds_svc_hdl_t *)arg == &ds_backup_handle)
97 		ds_primary_handle = hdl;
98 }
99 
100 static void
101 vc_unreg_handler(ds_cb_arg_t arg)
102 {
103 	if ((ds_svc_hdl_t *)arg == &ds_primary_handle)
104 		ds_primary_handle = DS_INVALID_HDL;
105 	else if ((ds_svc_hdl_t *)arg == &ds_backup_handle)
106 		ds_backup_handle = DS_INVALID_HDL;
107 }
108 
109 static void
110 vc_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
111 {
112 	_NOTE(ARGUNUSED(arg))
113 
114 	bcopy(buf, &promif_ds_resp, buflen);
115 	mutex_enter(&promif_prop_lock);
116 	cv_signal(&promif_prop_cv);
117 	mutex_exit(&promif_prop_lock);
118 }
119 
120 /*
121  * Initialize the linkage with DS (Domain Services).  We assume that
122  * the DS module has already been loaded by the platmod.
123  *
124  * The call to the DS init functions will eventually result in the
125  * invocation of our registration callback handlers, at which time DS
126  * is able to accept requests.
127  */
128 static void
129 promif_ds_init(void)
130 {
131 	static char *me = "promif_ds_init";
132 	int rv;
133 
134 	if ((ds_init =
135 	    (int (*)())modgetsymvalue("ds_cap_init", 0)) == 0) {
136 		cmn_err(CE_WARN, "%s: can't find ds_cap_init", me);
137 		return;
138 	}
139 
140 	if ((ds_send =
141 	    (int (*)())modgetsymvalue("ds_cap_send", 0)) == 0) {
142 		cmn_err(CE_WARN, "%s: can't find ds_cap_send", me);
143 		return;
144 	}
145 
146 	if ((rv = (*ds_init)(&vc_primary_cap, &vc_primary_ops)) != 0) {
147 		cmn_err(CE_NOTE,
148 		    "%s: ds_cap_init failed (primary): %d", me, rv);
149 	}
150 
151 
152 	if ((rv = (*ds_init)(&vc_backup_cap, &vc_backup_ops)) != 0) {
153 		cmn_err(CE_NOTE,
154 		    "%s: ds_cap_init failed (backup): %d", me, rv);
155 	}
156 }
157 
158 /*
159  * Prepare for ldom variable requests.
160  */
161 void
162 promif_prop_init(void)
163 {
164 	mutex_init(&promif_prop_lock, NULL, MUTEX_DEFAULT, NULL);
165 	cv_init(&promif_prop_cv, NULL, CV_DEFAULT, NULL);
166 
167 	promif_ds_init();
168 }
169 
170 
171 /*
172  * Replace the current value of a property string given its name and
173  * new value.
174  */
175 int
176 promif_ldom_setprop(char *name, void *value, int valuelen)
177 {
178 	var_config_msg_t *req;
179 	var_config_set_req_t *setp;
180 	var_config_cmd_t cmd;
181 	ds_svc_hdl_t ds_handle;
182 	int rv;
183 	int namelen = strlen(name);
184 	int paylen = namelen + 1 + valuelen; /* valuelen includes the null */
185 	static char *me = "promif_ldom_setprop";
186 
187 	if (ds_primary_handle != DS_INVALID_HDL)
188 		ds_handle = ds_primary_handle;
189 	else if (ds_backup_handle != DS_INVALID_HDL)
190 		ds_handle = ds_backup_handle;
191 	else
192 		return (-1);
193 
194 	req = kmem_zalloc(sizeof (var_config_hdr_t) + paylen, KM_SLEEP);
195 	req->var_config_cmd = VAR_CONFIG_SET_REQ;
196 	setp = &req->var_config_set;
197 	(void) strcpy(setp->name_and_value, name);
198 	(void) strncpy(&setp->name_and_value[namelen + 1], value, valuelen);
199 
200 	if ((rv = (*ds_send)(ds_handle, req,
201 	    sizeof (var_config_hdr_t) + paylen)) != 0) {
202 		cmn_err(CE_WARN, "%s: ds_cap_send failed: %d", me, rv);
203 		kmem_free(req, sizeof (var_config_hdr_t) + paylen);
204 		return (-1);
205 	}
206 
207 	kmem_free(req, sizeof (var_config_hdr_t) + paylen);
208 
209 	/*
210 	 * Since we are emulating OBP, we must comply with the promif
211 	 * infrastructure and execute only on the originating cpu.
212 	 */
213 	thread_affinity_set(curthread, CPU_CURRENT);
214 
215 	mutex_enter(&promif_prop_lock);
216 	if (cv_timedwait(&promif_prop_cv,
217 	    &promif_prop_lock, lbolt + PROMIF_DS_TIMEOUT_SEC * hz) == -1) {
218 		cmn_err(CE_WARN, "%s: ds response timeout", me);
219 		rv = -1;
220 		goto out;
221 	}
222 
223 	cmd = promif_ds_resp.vc_hdr.cmd;
224 	if (cmd != VAR_CONFIG_SET_RESP) {
225 		cmn_err(CE_WARN, "%s: bad response type: %d", me, cmd);
226 		rv = -1;
227 		goto out;
228 	}
229 	rv = (cfg_rsp->result == VAR_CONFIG_SUCCESS) ? valuelen : -1;
230 
231 out:
232 	mutex_exit(&promif_prop_lock);
233 	thread_affinity_clear(curthread);
234 	return (rv);
235 }
236 
237 int
238 promif_setprop(void *p)
239 {
240 	cell_t	*ci = (cell_t *)p;
241 	pnode_t node;
242 	caddr_t	name;
243 	caddr_t	value;
244 	int	len;
245 
246 	ASSERT(ci[1] == 4);
247 
248 	node  = p1275_cell2dnode(ci[3]);
249 	ASSERT(node == prom_optionsnode());
250 	name  = p1275_cell2ptr(ci[4]);
251 	value = p1275_cell2ptr(ci[5]);
252 	len = p1275_cell2int(ci[6]);
253 
254 	if (promif_stree_getproplen(node, name) != -1)
255 		len = promif_ldom_setprop(name, value, len);
256 
257 	if (len >= 0)
258 		len = promif_stree_setprop(node, name, (void *)value, len);
259 
260 
261 	ci[7] = p1275_int2cell(len);
262 
263 	return ((len == -1) ? len : 0);
264 }
265 
266 #endif
267 
268 int
269 promif_getprop(void *p)
270 {
271 	cell_t	*ci = (cell_t *)p;
272 	pnode_t	node;
273 	caddr_t	name;
274 	caddr_t	value;
275 	int	len;
276 
277 	ASSERT(ci[1] == 4);
278 
279 	node  = p1275_cell2dnode(ci[3]);
280 	name  = p1275_cell2ptr(ci[4]);
281 	value = p1275_cell2ptr(ci[5]);
282 
283 	len = promif_stree_getprop(node, name, value);
284 
285 	ci[7] = p1275_int2cell(len);
286 
287 	return ((len == -1) ? len : 0);
288 }
289 
290 int
291 promif_getproplen(void *p)
292 {
293 	cell_t	*ci = (cell_t *)p;
294 	pnode_t	node;
295 	caddr_t	name;
296 	int	len;
297 
298 	ASSERT(ci[1] == 2);
299 
300 	node = p1275_cell2dnode(ci[3]);
301 	name = p1275_cell2ptr(ci[4]);
302 
303 	len = promif_stree_getproplen(node, name);
304 
305 	ci[5] = p1275_int2cell(len);
306 
307 	return (0);
308 }
309 
310 int
311 promif_nextprop(void *p)
312 {
313 	cell_t	*ci = (cell_t *)p;
314 	pnode_t	node;
315 	caddr_t	prev;
316 	caddr_t	next;
317 
318 	ASSERT(ci[1] == 3);
319 
320 	node = p1275_cell2dnode(ci[3]);
321 	prev = p1275_cell2ptr(ci[4]);
322 	next = p1275_cell2ptr(ci[5]);
323 
324 	(void) promif_stree_nextprop(node, prev, next);
325 
326 	return (0);
327 }
328