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