xref: /illumos-gate/usr/src/cmd/drd/drd.c (revision 8bd3a292467a7ce00ecf774f887442a82e26e438)
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 2008 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 DR daemon
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <libgen.h>
41 #include <syslog.h>
42 #include <door.h>
43 #include <assert.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 
47 #include <sys/drctl_impl.h>
48 #include <sys/drctl.h>
49 #include "drd.h"
50 
51 boolean_t drd_debug = B_FALSE;
52 boolean_t drd_daemonized = B_FALSE;
53 
54 #define	DRD_DOOR_FILE		"/tmp/drd_door"
55 #define	DRD_DOOR_RETURN_ERR()	(void) door_return(NULL, 0, NULL, 0)
56 
57 static char *cmdname;
58 static int drctl_fd;
59 static drctl_rsrc_t *drd_result = NULL;
60 
61 /*
62  * Currently, the only supported backend is for the Reconfiguration
63  * Coordination Manager (RCM). When there are other backends, this
64  * variable should be set dynamically.
65  */
66 static drd_backend_t *drd_backend = &drd_rcm_backend;
67 
68 static void drd_daemonize(void);
69 static int drd_init_drctl_dev(boolean_t standalone);
70 static int drd_init_door_server(boolean_t standalone);
71 static void drd_door_server(void *, char *, size_t, door_desc_t *, uint_t);
72 
73 int
74 main(int argc, char **argv)
75 {
76 	int		opt;
77 	boolean_t	standalone = B_FALSE;
78 
79 	cmdname = basename(argv[0]);
80 
81 	/*
82 	 * Process command line arguments
83 	 */
84 	opterr = 0;	/* disable getopt error messages */
85 	while ((opt = getopt(argc, argv, "ds")) != EOF) {
86 
87 		switch (opt) {
88 		case 'd':
89 			drd_debug = B_TRUE;
90 			break;
91 		case 's':
92 			standalone = B_TRUE;
93 			break;
94 		default:
95 			drd_err("unkown option: -%c", optopt);
96 			exit(1);
97 		}
98 	}
99 
100 	drd_dbg("initializing %s...", cmdname);
101 
102 	/* must be root */
103 	if (geteuid() != 0) {
104 		drd_err("permission denied: must run as root");
105 		exit(1);
106 	}
107 
108 	/* open the drctl device */
109 	if (drd_init_drctl_dev(standalone) != 0) {
110 		drd_err("unable to initialize drctl device");
111 		exit(1);
112 	}
113 
114 	/* daemonize */
115 	if (!standalone) {
116 		drd_daemonize();
117 	}
118 
119 	/* initialize door server */
120 	if (drd_init_door_server(standalone) != 0) {
121 		drd_err("unable to initialize door server");
122 		exit(1);
123 	}
124 
125 	/* initialize the backend */
126 	if ((*drd_backend->init)() != 0) {
127 		drd_err("unable to initialize backend processor");
128 		exit(1);
129 	}
130 
131 	/* loop forever */
132 	for (;;) {
133 		pause();
134 	}
135 
136 	/*NOTREACHED*/
137 	return (0);
138 }
139 
140 static void
141 drd_daemonize(void)
142 {
143 	pid_t	pid;
144 
145 	if ((pid = fork()) == -1) {
146 		drd_err("failed to fork: %s", strerror(errno));
147 		exit(1);
148 	}
149 
150 	if (pid != 0) {
151 		/* parent */
152 		exit(0);
153 	}
154 
155 	/*
156 	 * Initialize child process
157 	 */
158 	(void) setsid();
159 	(void) chdir("/");
160 	(void) umask(0);
161 
162 	/*
163 	 * Initialize file descriptors. Do not touch stderr
164 	 * which is initialized by SMF to point to the drd
165 	 * specific log file.
166 	 */
167 	assert(drctl_fd == (STDERR_FILENO + 1));
168 
169 	(void) close(STDIN_FILENO);
170 	(void) open("/dev/null", O_RDWR);
171 	(void) dup2(STDIN_FILENO, STDOUT_FILENO);
172 
173 	closefrom(drctl_fd + 1);
174 
175 	/* initialize logging */
176 	openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON);
177 
178 	drd_daemonized = B_TRUE;
179 }
180 
181 static int
182 drd_init_drctl_dev(boolean_t standalone)
183 {
184 	void (*drd_output)(char *, ...);
185 
186 	drd_output = (standalone) ? drd_info : drd_err;
187 
188 	/* open the drctl device */
189 	if ((drctl_fd = open(DRCTL_DEV, O_RDWR)) == -1) {
190 		drd_output("open %s failed: %s", DRCTL_DEV, strerror(errno));
191 		return ((standalone) ? 0 : -1);
192 	}
193 
194 	return (0);
195 }
196 
197 static int
198 drd_init_door_server(boolean_t standalone)
199 {
200 	int		door_fd;
201 	int		dbg_fd;
202 	drctl_setup_t	setup;
203 
204 	assert((drctl_fd != -1) || standalone);
205 
206 	/* create the door */
207 	if ((door_fd = door_create(drd_door_server, NULL, 0)) == -1) {
208 		drd_err("door_create failed: %s", strerror(errno));
209 		return (-1);
210 	}
211 
212 	if (drctl_fd != -1) {
213 
214 		setup.did = door_fd;
215 
216 		/* send the door descriptor to drctl */
217 		if (ioctl(drctl_fd, DRCTL_IOCTL_CONNECT_SERVER, &setup) == -1) {
218 			drd_err("drctl ioctl failed: %s", strerror(errno));
219 			(void) door_revoke(door_fd);
220 			return (-1);
221 		}
222 
223 		drd_dbg("connection to drctl established");
224 
225 		/* setup is complete in daemon mode */
226 		if (!standalone) {
227 			return (0);
228 		}
229 	}
230 
231 	/*
232 	 * At this point, the daemon is running in standalone
233 	 * mode for testing purposes. This allows the daemon
234 	 * to be controlled directly through a door exported
235 	 * to the filesystem. No drctl device is required in
236 	 * this mode.
237 	 */
238 
239 	/* create the door file */
240 	unlink(DRD_DOOR_FILE);
241 	if ((dbg_fd = creat(DRD_DOOR_FILE, 0644)) == -1) {
242 		drd_err("failed to create door file '%s': %s",
243 		    DRD_DOOR_FILE, strerror(errno));
244 		(void) door_revoke(door_fd);
245 		return (-1);
246 	}
247 	close(dbg_fd);
248 
249 	/* attach the door file to the door descriptor */
250 	if (fattach(door_fd, DRD_DOOR_FILE) == -1) {
251 		drd_err("failed to fattach door file '%s': %s",
252 		    DRD_DOOR_FILE, strerror(errno));
253 		unlink(DRD_DOOR_FILE);
254 		(void) door_revoke(door_fd);
255 		return (-1);
256 	}
257 
258 	drd_dbg("door server attached to '%s'", DRD_DOOR_FILE);
259 
260 	return (0);
261 }
262 
263 static size_t
264 drd_pack_response(drctl_rsrc_t *rsrcs, int nrsrc)
265 {
266 	drctl_rsrc_t	*orsrcsp;
267 	void		*resizep;
268 	size_t		osize;
269 	char		*str;
270 	size_t		offset;
271 	char		*off;
272 	int		idx;
273 	size_t		len;
274 
275 	drd_dbg("drd_pack_response...");
276 
277 	/*
278 	 * Deallocate the global response buffer if it is
279 	 * in use. This assumes that there will only ever
280 	 * be one pending operation in the daemon. This is
281 	 * enforced by the kernel.
282 	 */
283 	s_free(drd_result);
284 
285 	orsrcsp = calloc(sizeof (*orsrcsp), nrsrc);
286 	osize = sizeof (*orsrcsp) * nrsrc;
287 	bcopy(rsrcs, orsrcsp, osize);
288 
289 	offset = osize;
290 
291 	/*
292 	 * Loop through all the resources and concatenate
293 	 * all the error strings to the end of the resource
294 	 * array. Also, update the offset field of each
295 	 * resource.
296 	 */
297 	for (idx = 0; idx < nrsrc; idx++) {
298 
299 		str = (char *)(uintptr_t)rsrcs[idx].offset;
300 
301 		/* skip if no error string */
302 		if (str == NULL)
303 			continue;
304 
305 		len = strlen(str) + 1;
306 
307 		/* increase the size of the buffer */
308 		resizep = realloc(orsrcsp, osize + len);
309 		if (resizep == NULL) {
310 			drd_err("realloc failed: %s", strerror(errno));
311 			s_free(orsrcsp);
312 
313 			/* clean up any remaining strings */
314 			while (idx < nrsrc) {
315 				str = (char *)(uintptr_t)rsrcs[idx++].offset;
316 				s_free(str);
317 			}
318 			return (0);
319 		}
320 
321 		orsrcsp = resizep;
322 
323 		/* copy the error string into the response */
324 		off = (char *)orsrcsp + offset;
325 		bcopy(str, off, len);
326 		orsrcsp[idx].offset = offset;
327 
328 		/*
329 		 * Now that the error string has been copied
330 		 * into the response message, the memory that
331 		 * was allocated for it is no longer needed.
332 		 */
333 		s_free(str);
334 		rsrcs[idx].offset = 0;
335 
336 		/* update size and offset */
337 		offset += len;
338 		osize += len;
339 	}
340 
341 	drd_result = orsrcsp;
342 	return (osize);
343 }
344 
345 /*ARGSUSED*/
346 static void
347 drd_door_server(void *cookie, char *argp, size_t arg_sz, door_desc_t *dp,
348     uint_t n_desc)
349 {
350 	drd_msg_t	*msg = (drd_msg_t *)(uintptr_t)argp;
351 	drctl_rsrc_t	*rsrcs;
352 	size_t		osize;
353 	int		nrsrc;
354 
355 	drd_dbg("drd_door_server...");
356 	drd_dbg("message received: %d bytes", arg_sz);
357 
358 	/* sanity check incoming arg */
359 	if ((argp == NULL) || (arg_sz == 0))
360 		DRD_DOOR_RETURN_ERR();
361 
362 	drd_dbg("  cmd=%d, count=%d, flags=%d", msg->cmd,
363 	    msg->count, msg->flags);
364 
365 	rsrcs = (drctl_rsrc_t *)(uintptr_t)msg->data;
366 	nrsrc = msg->count;
367 
368 	/* pass off to backend for processing */
369 	switch (msg->cmd) {
370 	case DRCTL_CPU_CONFIG_REQUEST:
371 		(*drd_backend->cpu_config_request)(rsrcs, nrsrc);
372 		break;
373 
374 	case DRCTL_CPU_CONFIG_NOTIFY:
375 		(*drd_backend->cpu_config_notify)(rsrcs, nrsrc);
376 		break;
377 
378 	case DRCTL_CPU_UNCONFIG_REQUEST:
379 		(*drd_backend->cpu_unconfig_request)(rsrcs, nrsrc);
380 		break;
381 
382 	case DRCTL_CPU_UNCONFIG_NOTIFY:
383 		(*drd_backend->cpu_unconfig_notify)(rsrcs, nrsrc);
384 		break;
385 
386 	case DRCTL_MEM_CONFIG_REQUEST:
387 	case DRCTL_MEM_CONFIG_NOTIFY:
388 	case DRCTL_MEM_UNCONFIG_REQUEST:
389 	case DRCTL_MEM_UNCONFIG_NOTIFY:
390 		drd_err("memory DR operations not supported yet");
391 		DRD_DOOR_RETURN_ERR();
392 		break;
393 
394 	case DRCTL_IO_CONFIG_REQUEST:
395 		(*drd_backend->io_config_request)(rsrcs, nrsrc);
396 		break;
397 
398 	case DRCTL_IO_CONFIG_NOTIFY:
399 		(*drd_backend->io_config_notify)(rsrcs, nrsrc);
400 		break;
401 
402 	case DRCTL_IO_UNCONFIG_REQUEST:
403 		(*drd_backend->io_unconfig_request)(rsrcs, nrsrc);
404 		break;
405 
406 	case DRCTL_IO_UNCONFIG_NOTIFY:
407 		(*drd_backend->io_unconfig_notify)(rsrcs, nrsrc);
408 		break;
409 
410 	default:
411 		drd_err("unknown command: %d", msg->cmd);
412 		DRD_DOOR_RETURN_ERR();
413 		break;
414 	}
415 
416 	osize = drd_pack_response(rsrcs, nrsrc);
417 	if (osize == 0)
418 		DRD_DOOR_RETURN_ERR();
419 
420 	(void) door_return((char *)drd_result, osize, NULL, 0);
421 }
422