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