xref: /illumos-gate/usr/src/uts/sun4v/io/platsvc.c (revision 1a220b56b93ff1dc80855691548503117af4cc10)
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 /*
30  * sun4v Platform Services Module
31  */
32 
33 #include <sys/modctl.h>
34 #include <sys/cmn_err.h>
35 #include <sys/machsystm.h>
36 #include <sys/note.h>
37 #include <sys/uadmin.h>
38 #include <sys/ds.h>
39 #include <sys/platsvc.h>
40 
41 /*
42  * Debugging routines
43  */
44 #ifdef DEBUG
45 uint_t ps_debug = 0x0;
46 #define	DBG	if (ps_debug) printf
47 #else /* DEBUG */
48 #define	DBG	_NOTE(CONSTCOND) if (0) printf
49 #endif /* DEBUG */
50 
51 /*
52  * Time resolution conversions.
53  */
54 #define	MS2NANO(x)	((x) * MICROSEC)
55 #define	MS2SEC(x)	((x) / MILLISEC)
56 #define	MS2MIN(x)	(MS2SEC(x) / 60)
57 
58 /*
59  * Domains Services interaction
60  */
61 static ds_svc_hdl_t	ds_md_handle;
62 static ds_svc_hdl_t	ds_shutdown_handle;
63 static ds_svc_hdl_t	ds_panic_handle;
64 
65 static ds_ver_t		ps_vers[] = {{ 1, 0 }};
66 #define	PS_NVERS	(sizeof (ps_vers) / sizeof (ps_vers[0]))
67 
68 static ds_capability_t ps_md_cap = {
69 	"md-update",		/* svc_id */
70 	ps_vers,		/* vers */
71 	PS_NVERS		/* nvers */
72 };
73 
74 static ds_capability_t ps_shutdown_cap = {
75 	"domain-shutdown",	/* svc_id */
76 	ps_vers,		/* vers */
77 	PS_NVERS		/* nvers */
78 };
79 
80 static ds_capability_t ps_panic_cap = {
81 	"domain-panic",		/* svc_id */
82 	ps_vers,		/* vers */
83 	PS_NVERS		/* nvers */
84 };
85 
86 static void ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl);
87 static void ps_unreg_handler(ds_cb_arg_t arg);
88 
89 static void ps_md_data_handler(ds_cb_arg_t arg, void * buf, size_t buflen);
90 static void ps_shutdown_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
91 static void ps_panic_data_handler(ds_cb_arg_t arg, void * buf, size_t buflen);
92 
93 static ds_clnt_ops_t ps_md_ops = {
94 	ps_reg_handler,			/* ds_reg_cb */
95 	ps_unreg_handler,		/* ds_unreg_cb */
96 	ps_md_data_handler,		/* ds_data_cb */
97 	&ds_md_handle			/* cb_arg */
98 };
99 
100 static ds_clnt_ops_t ps_shutdown_ops = {
101 	ps_reg_handler,			/* ds_reg_cb */
102 	ps_unreg_handler,		/* ds_unreg_cb */
103 	ps_shutdown_data_handler,	/* ds_data_cb */
104 	&ds_shutdown_handle		/* cb_arg */
105 };
106 
107 static ds_clnt_ops_t ps_panic_ops = {
108 	ps_reg_handler,			/* ds_reg_cb */
109 	ps_unreg_handler,		/* ds_unreg_cb */
110 	ps_panic_data_handler,		/* ds_data_cb */
111 	&ds_panic_handle		/* cb_arg */
112 };
113 
114 static int ps_init(void);
115 static void ps_fini(void);
116 
117 /*
118  * Powerdown timeout value of 5 minutes.
119  */
120 #define	PLATSVC_POWERDOWN_DELAY		1200
121 
122 static struct modlmisc modlmisc = {
123 	&mod_miscops,
124 	"sun4v Platform Services %I%"
125 };
126 
127 static struct modlinkage modlinkage = {
128 	MODREV_1,
129 	(void *)&modlmisc,
130 	NULL
131 };
132 
133 int
134 _init(void)
135 {
136 	int	rv;
137 
138 	if ((rv = ps_init()) != 0)
139 		return (rv);
140 
141 	if ((rv = mod_install(&modlinkage)) != 0)
142 		ps_fini();
143 
144 	return (rv);
145 }
146 
147 int
148 _info(struct modinfo *modinfop)
149 {
150 	return (mod_info(&modlinkage, modinfop));
151 }
152 
153 int platsvc_allow_unload;
154 
155 int
156 _fini(void)
157 {
158 	int	status;
159 
160 	if (platsvc_allow_unload == 0)
161 		return (EBUSY);
162 
163 	if ((status = mod_remove(&modlinkage)) == 0)
164 		ps_fini();
165 
166 	return (status);
167 }
168 
169 static int
170 ps_init(void)
171 {
172 	int	rv;
173 	extern int mdeg_init(void);
174 
175 	/* register with domain services framework */
176 	rv = ds_cap_init(&ps_md_cap, &ps_md_ops);
177 	if (rv != 0) {
178 		cmn_err(CE_WARN, "ds_cap_init md-update failed: %d", rv);
179 		return (rv);
180 	}
181 
182 	rv = ds_cap_init(&ps_shutdown_cap, &ps_shutdown_ops);
183 	if (rv != 0) {
184 		cmn_err(CE_WARN, "ds_cap_init domain-shutdown failed: %d", rv);
185 		(void) ds_cap_fini(&ps_md_cap);
186 		return (rv);
187 	}
188 
189 	rv = ds_cap_init(&ps_panic_cap, &ps_panic_ops);
190 	if (rv != 0) {
191 		cmn_err(CE_WARN, "ds_cap_init domain-panic failed: %d", rv);
192 		(void) ds_cap_fini(&ps_md_cap);
193 		(void) ds_cap_fini(&ps_shutdown_cap);
194 		return (rv);
195 	}
196 
197 	rv = mdeg_init();
198 
199 	return (rv);
200 }
201 
202 static void
203 ps_fini(void)
204 {
205 	extern void mdeg_fini(void);
206 
207 	/*
208 	 * Stop incoming requests from Zeus
209 	 */
210 	(void) ds_cap_fini(&ps_md_cap);
211 	(void) ds_cap_fini(&ps_shutdown_cap);
212 	(void) ds_cap_fini(&ps_panic_cap);
213 
214 	mdeg_fini();
215 }
216 
217 static void
218 ps_md_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
219 {
220 	extern int mach_descrip_update(void);
221 	extern void mdeg_notify_clients(void);
222 
223 	ds_svc_hdl_t		 ds_handle;
224 	platsvc_md_update_req_t	 *msg = buf;
225 	platsvc_md_update_resp_t resp_msg;
226 	uint_t			 rv;
227 
228 	if (arg == NULL)
229 		return;
230 
231 	ds_handle = ds_md_handle;
232 
233 	if (msg == NULL || buflen != sizeof (platsvc_md_update_req_t)) {
234 		resp_msg.req_num = 0;
235 		resp_msg.result = MD_UPDATE_INVALID_MSG;
236 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
237 		    sizeof (resp_msg))) != 0) {
238 			cmn_err(CE_NOTE, "md ds_cap_send failed (%d)", rv);
239 		}
240 		return;
241 	}
242 
243 	DBG("MD Reload...\n");
244 	if (mach_descrip_update()) {
245 		cmn_err(CE_WARN, "MD reload failed\n");
246 		return;
247 	}
248 
249 	/*
250 	 * notify registered clients that MD has
251 	 * been updated
252 	 */
253 	mdeg_notify_clients();
254 
255 	resp_msg.req_num = msg->req_num;
256 	resp_msg.result = MD_UPDATE_SUCCESS;
257 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
258 		cmn_err(CE_NOTE, "md ds_cap_send resp failed (%d)", rv);
259 	}
260 }
261 
262 static void
263 ps_shutdown_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
264 {
265 	ds_svc_hdl_t		ds_handle;
266 	platsvc_shutdown_req_t	*msg = buf;
267 	platsvc_shutdown_resp_t	resp_msg;
268 	uint_t			rv;
269 	hrtime_t		start;
270 
271 	if (arg == NULL)
272 		return;
273 
274 	ds_handle = ds_shutdown_handle;
275 
276 	if (msg == NULL || buflen != sizeof (platsvc_shutdown_req_t)) {
277 		resp_msg.req_num = 0;
278 		resp_msg.result = DOMAIN_SHUTDOWN_INVALID_MSG;
279 		resp_msg.reason[0] = '\0';
280 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
281 		    sizeof (resp_msg))) != 0) {
282 			cmn_err(CE_NOTE, "shutdown ds_cap_send failed (%d)",
283 			    rv);
284 		}
285 		return;
286 	}
287 
288 	resp_msg.req_num = msg->req_num;
289 	resp_msg.result = DOMAIN_SHUTDOWN_SUCCESS;
290 	resp_msg.reason[0] = '\0';
291 
292 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
293 		cmn_err(CE_NOTE, "shutdown ds_cap_send resp failed (%d)", rv);
294 	}
295 
296 	/*
297 	 * Honor the ldoms manager's shutdown delay requirement.
298 	 */
299 	cmn_err(CE_NOTE, "shutdown requested by ldom manager, "
300 	    "system shutdown in %d minutes", MS2MIN(msg->delay));
301 
302 	start = gethrtime();
303 	while (gethrtime() - start < MS2NANO(msg->delay))
304 		;
305 
306 	(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
307 }
308 
309 
310 static void
311 ps_panic_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
312 {
313 	ds_svc_hdl_t		ds_handle;
314 	platsvc_panic_req_t	*msg = buf;
315 	platsvc_panic_resp_t	resp_msg;
316 	uint_t			rv;
317 
318 	if (arg == NULL)
319 		return;
320 
321 	ds_handle = ds_panic_handle;
322 
323 	if (msg == NULL || buflen != sizeof (platsvc_panic_req_t)) {
324 		resp_msg.req_num = 0;
325 		resp_msg.result = DOMAIN_PANIC_INVALID_MSG;
326 		resp_msg.reason[0] = '\0';
327 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
328 		    sizeof (resp_msg))) != 0) {
329 			cmn_err(CE_NOTE, "panic ds_cap_send resp failed (%d)",
330 			    rv);
331 		}
332 		return;
333 	}
334 
335 	resp_msg.req_num = msg->req_num;
336 	resp_msg.result = DOMAIN_PANIC_SUCCESS;
337 	resp_msg.reason[0] = '\0';
338 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
339 		cmn_err(CE_NOTE, "panic ds_cap_send resp failed (%d)", rv);
340 	}
341 
342 	cmn_err(CE_PANIC, "Panic forced by ldom manager");
343 	_NOTE(NOTREACHED)
344 }
345 
346 static void
347 ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
348 {
349 	DBG("ps_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
350 	    arg, ver->major, ver->minor, hdl);
351 
352 	if ((ds_svc_hdl_t *)arg == &ds_md_handle)
353 		ds_md_handle = hdl;
354 	if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
355 		ds_shutdown_handle = hdl;
356 	if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
357 		ds_panic_handle = hdl;
358 }
359 
360 static void
361 ps_unreg_handler(ds_cb_arg_t arg)
362 {
363 	DBG("ps_unreg_handler: arg=0x%p\n", arg);
364 
365 	if ((ds_svc_hdl_t *)arg == &ds_md_handle)
366 		ds_md_handle = DS_INVALID_HDL;
367 	if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
368 		ds_shutdown_handle = DS_INVALID_HDL;
369 	if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
370 		ds_panic_handle = DS_INVALID_HDL;
371 }
372