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
49boolean_t drd_debug = B_FALSE;
50boolean_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
55static char *cmdname;
56static int drctl_fd;
57static 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 */
64static drd_backend_t *drd_backend = &drd_rcm_backend;
65
66static void drd_daemonize(void);
67static int drd_init_drctl_dev(boolean_t standalone);
68static int drd_init_door_server(boolean_t standalone);
69static void drd_door_server(void *, char *, size_t, door_desc_t *, uint_t);
70
71int
72main(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
138static void
139drd_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
179static int
180drd_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
195static int
196drd_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
261static size_t
262drd_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*/
344static void
345drd_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