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